]> git.neil.brown.name Git - history.git/commitdiff
[PATCH] v4l: major bttv update
authorGerd Knorr <kraxel@bytesex.org>
Wed, 13 Mar 2002 03:33:04 +0000 (19:33 -0800)
committerLinus Torvalds <torvalds@penguin.transmeta.com>
Wed, 13 Mar 2002 03:33:04 +0000 (19:33 -0800)
This is a major update of the bttv core (0.7.x to 0.8.x).  There are way
too many changes to list them all, the complete core code for video
frame capture has been rewritten from scratch.

drivers/media/video/Makefile
drivers/media/video/bttv-cards.c
drivers/media/video/bttv-driver.c
drivers/media/video/bttv-risc.c [new file with mode: 0644]
drivers/media/video/bttv-vbi.c [new file with mode: 0644]
drivers/media/video/bttv.h
drivers/media/video/bttvp.h

index a0d5cf9d3db5a7866b83451237d752ffa92bf5c2..75d5bc7183c5f67085d613efa48ca3f26ee62e05 100644 (file)
@@ -28,7 +28,8 @@ O_TARGET := video.o
 export-objs     :=     i2c-old.o videodev.o bttv-if.o cpia.o video-buf.o
 
 list-multi     :=      bttv.o zoran.o
-bttv-objs      :=      bttv-driver.o bttv-cards.o bttv-if.o
+bttv-objs      :=      bttv-driver.o bttv-cards.o bttv-if.o \
+                       bttv-risc.o bttv-vbi.o
 zoran-objs      :=     zr36120.o zr36120_i2c.o zr36120_mem.o
 
 obj-$(CONFIG_VIDEO_DEV) += videodev.o
index 67677ae5b8cbb7089be868456ee1dac7be4cb219..54cf158b9355df96c5298ebeae77996a3ef94035 100644 (file)
@@ -39,9 +39,9 @@
 #include "tuner.h"
 
 /* fwd decl */
+static void boot_msp34xx(struct bttv *btv, int pin);
 static void hauppauge_eeprom(struct bttv *btv);
 static void avermedia_eeprom(struct bttv *btv);
-
 static void init_PXC200(struct bttv *btv);
 #if 0
 static void init_tea5757(struct bttv *btv);
@@ -55,6 +55,9 @@ static void terratv_audio(struct bttv *btv, struct video_audio *v, int set);
 static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set);
 static void winfast2000_audio(struct bttv *btv, struct video_audio *v, int set);
 static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set);
+static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set);
+static void windvr_audio(struct bttv *btv, struct video_audio *v, int set);
+static void rv605_muxsel(struct bttv *btv, unsigned int input);
 
 /* config variables */
 static int triton1=0;
@@ -77,7 +80,8 @@ MODULE_PARM(triton1,"i");
 MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
                 "[enable bug compatibility for triton1 + others]");
 MODULE_PARM(vsfx,"i");
-MODULE_PARM_DESC(vsfx,"set VSFX pci config bit [yet another chipset flaw workaround]");
+MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
+                "[yet another chipset flaw workaround]");
 MODULE_PARM(no_overlay,"i");
 MODULE_PARM(card,"1-4i");
 MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
@@ -151,7 +155,7 @@ static struct CARD {
        { 0x00041461, BTTV_AVERMEDIA98,   "AVerMedia TVCapture 98" },
 
        { 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" },
-       { 0x300214ff, BTTV_PHOEBE_TVMAS,  "Phoebe TV Master" },
+       { 0x300214ff, BTTV_PHOEBE_TVMAS,  "Phoebe TV Master (CPH060)" },
 
        { 0x1117153b, BTTV_TERRATVALUE,   "Terratec TValue" },
        { 0x1118153b, BTTV_TERRATVALUE,   "Terratec TValue" },
@@ -161,6 +165,7 @@ static struct CARD {
        { 0x1127153b, BTTV_TERRATV,       "Terratec TV+"    },
        { 0x1134153b, BTTV_TERRATVALUE,   "Terratec TValue" },
        { 0x1135153b, BTTV_TERRATVALUER,  "Terratec TValue Radio" },
+       { 0x5018153b, BTTV_TERRATVALUE,   "Terratec TValue" },
 
        { 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
        { 0x400d15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
@@ -168,12 +173,14 @@ static struct CARD {
        { 0x401615b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
 
        { 0x010115cb, BTTV_GMV1,          "AG GMV1" },
-       { 0x010114c7, BTTV_MODTEC_205,    "Modular Technology PCTV" },
+       { 0x010114c7, BTTV_MODTEC_205,    "Modular Technology MM205 PCTV" },
        { 0x18501851, BTTV_CHRONOS_VS2,   "Flyvideo 98 (LR50)/ Chronos Video Shuttle II" },
        { 0x18511851, BTTV_FLYVIDEO98EZ,  "Flyvideo 98EZ (LR51)/ CyberMail AV" },
        { 0x18521852, BTTV_TYPHOON_TVIEW, "Flyvideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" },
        { 0x10b42636, BTTV_HAUPPAUGE878,  "STB ???" },
        { 0x217d6606, BTTV_WINFAST2000,   "Leadtek WinFast TV 2000" },
+       { 0x03116000, BTTV_SENSORAY311,   "Sensoray 311" },
+       { 0x00790e11, BTTV_WINDVR,        "Canopus WinDVR PCI" },
 
        { 0, -1, NULL }
 };
@@ -276,8 +283,8 @@ struct tvcard bttv_tvcards[] = {
 },{
 
 /* ---- card 0x08 ---------------------------------- */
-       name:           "Fly Video II (Bt848)",
-       video_inputs:   3,
+       name:           "FlyVideo II (Bt848) LR26",
+       video_inputs:   4,
        audio_inputs:   1,
        tuner:          0,
        svhs:           2,
@@ -285,9 +292,10 @@ struct tvcard bttv_tvcards[] = {
        muxsel:         { 2, 3, 1, 1},
        audiomux:       { 0, 0xc00, 0x800, 0x400, 0xc00, 0},
        needs_tvaudio:  1,
+       pll:            PLL_28,
        tuner_type:     -1,
 },{
-       name:           "TurboTV",
+       name:           "IXMicro TurboTV",
        video_inputs:   3,
        audio_inputs:   1,
        tuner:          0,
@@ -346,14 +354,14 @@ struct tvcard bttv_tvcards[] = {
        pll:            PLL_28,
        tuner_type:     -1,
 },{
-       name:           "Aimslab VHX",
+       name:           "Aimslab Video Highway Xtreme (VHX)",
        video_inputs:   3,
        audio_inputs:   1,
        tuner:          0,
        svhs:           2,
        gpiomask:       7,
        muxsel:         { 2, 3, 1, 1},
-       audiomux:       { 0, 1, 2, 3, 4},
+       audiomux:       { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */
        needs_tvaudio:  1,
        tuner_type:     -1,
 },{
@@ -393,6 +401,7 @@ struct tvcard bttv_tvcards[] = {
        needs_tvaudio:  1,
        tuner_type:     -1,
        audio_hook:     winview_audio,
+       has_radio:      1,
 },{
        name:           "AVEC Intercapture",
        video_inputs:   3,
@@ -438,7 +447,7 @@ struct tvcard bttv_tvcards[] = {
        pll:            PLL_28,
        tuner_type:     TUNER_PHILIPS_PAL_I,
 },{
-       name:           "Phoebe Tv Master + FM",
+       name:           "Phoebe Tv Master + FM (CPH050)",
        video_inputs:   3,
        audio_inputs:   1,
        tuner:          0,
@@ -752,6 +761,7 @@ struct tvcard bttv_tvcards[] = {
        gpiomask:       0x1f0000,
        muxsel:         { 2, 3, 1, 1},
        audiomux:       { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff },
+       needs_tvaudio:  1,
        no_msp34xx:     1,
        pll:            PLL_35,
        tuner_type:     1,
@@ -788,7 +798,7 @@ struct tvcard bttv_tvcards[] = {
        video_inputs:   4,
        audio_inputs:   1,
        tuner:          0,
-       svhs:           2,
+       svhs:           3,
        gpiomask:       0xAA0000,
        muxsel:         { 2,3,1,1 },
        audiomux:       { 0x20000, 0, 0x80000, 0x80000, 0xa8000, 0x46000  },
@@ -860,7 +870,7 @@ struct tvcard bttv_tvcards[] = {
 },{
        /* Miguel Angel Alvarez <maacruz@navegalia.com>
           old Easy TV BT848 version (model CPH031) */
-       name:           "BESTBUY Easy TV",
+       name:           "BESTBUY Easy TV (CPH031)",
        video_inputs:   4,
        audio_inputs:   1,
        tuner:          0,
@@ -890,7 +900,7 @@ struct tvcard bttv_tvcards[] = {
        /* This is the ultimate cheapo capture card 
         * just a BT848A on a small PCB!
         * Steve Hosgood <steve@equiinet.com> */
-       name:           "GrandTec 'Grand Video Capture'",
+       name:           "GrandTec 'Grand Video Capture' (Bt848)",
        video_inputs:   2,
        audio_inputs:   0,
        tuner:          -1,
@@ -904,7 +914,7 @@ struct tvcard bttv_tvcards[] = {
        tuner_type:     -1,
 },{
         /* Daniel Herrington <daniel.herrington@home.com> */
-        name:           "Phoebe TV Master Only (No FM)",
+        name:           "Phoebe TV Master Only (No FM) CPH060",
         video_inputs:   3,
         audio_inputs:   1,
         tuner:          0,
@@ -917,7 +927,7 @@ struct tvcard bttv_tvcards[] = {
         tuner_type:     TUNER_TEMIC_4036FY5_NTSC,
 },{
        /* Matti Mottus <mottus@physic.ut.ee> */
-       name:           "TV Capturer",
+       name:           "TV Capturer (CPH03X)",
        video_inputs:   4,
        audio_inputs:   1,
        tuner:          0,
@@ -931,7 +941,7 @@ struct tvcard bttv_tvcards[] = {
 
 /* ---- card 0x3c ---------------------------------- */
        /* Philip Blundell <philb@gnu.org> */
-       name:           "MM100PCTV",
+       name:           "Modular Technology MM100PCTV",
        video_inputs:   2,
        audio_inputs:   2,
        gpiomask:       11,
@@ -977,7 +987,7 @@ struct tvcard bttv_tvcards[] = {
        tuner:          0,
        svhs:           2,
        gpiomask:       0xf03f,
-       muxsel:         { 2, 3, 0, 1},
+       muxsel:         { 2, 3, 1, 0 },
        audiomux:       { 0xbffe, 0, 0xbfff, 0, 0xbffe},
        pll:            PLL_28,
        tuner_type:     TUNER_TEMIC_4006FN5_MULTI_PAL,
@@ -1005,7 +1015,13 @@ struct tvcard bttv_tvcards[] = {
        svhs:           2,
        gpiomask:       0x18e0,
        muxsel:         { 2, 3, 0, 1},
-       audiomux:       { 0,0x18e0,0x1000,0x1000,0x1080, 0x1080 },
+                       /* Radio changed from 1e80 to 0x800 to make
+                          Flyvideo2000S in .hu happy (gm)*/
+                       /* -dk-???: set mute=0x1800 for tda9874h daughterboard */
+       audiomux:       { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 },
+       audio_hook:     fv2000s_audio,
+       no_msp34xx:     1,
+       no_tda9875:     1,
        needs_tvaudio:  1,
        pll:            PLL_28,
        tuner_type:     5,
@@ -1047,11 +1063,12 @@ struct tvcard bttv_tvcards[] = {
         tuner:          0,
         svhs:           -1,
         gpiomask:       0x4f8a00,
-                       // 0x100000: 1=MSP enabled (0=disable again)
-                       // 0x010000: somehow influences tuner picture quality (?)
-        audiomux:       {0x947fff, 0x987fff,0x947fff,0x947fff},
-                        //tvtuner, radio,   external,internal,mute,stereo
-        muxsel:         { 2, 3 ,0 ,1}, /* tuner, Composit, SVid, Composit-on-Svid-adapter*/
+       // 0x100000: 1=MSP enabled (0=disable again)
+       // 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC)
+        audiomux:       {0x947fff, 0x987fff,0x947fff,0x947fff, 0x947fff},
+       // tvtuner, radio,   external,internal, mute,  stereo
+       /* tuner, Composit, SVid, Composit-on-Svid-adapter*/
+        muxsel:         { 2, 3 ,0 ,1},
         tuner_type:     TUNER_MT2032,
        pll:            PLL_28,
        has_radio:      1,
@@ -1106,8 +1123,74 @@ struct tvcard bttv_tvcards[] = {
        tuner_type:     -1,
        audio_hook:     pvbt878p9b_audio,
        has_radio:      1,
-}
-};
+},{
+       /* Clay Kunz <ckunz@mail.arc.nasa.gov> */
+       /* you must jumper JP5 for the card to work */
+       name:           "Sensoray 311",
+       video_inputs:   5,
+       audio_inputs:   0,
+       tuner:          -1,
+       svhs:           4,
+       gpiomask:       0,
+       muxsel:         { 2, 3, 1, 0, 0},
+       audiomux:       { 0 },
+       needs_tvaudio:  0,
+       tuner_type:     -1,
+},{
+       /* Miguel Freitas <miguel@cetuc.puc-rio.br> */
+       name:           "RemoteVision MX (RV605)",
+       video_inputs:   16,
+       audio_inputs:   0,
+       tuner:          0,
+       svhs:           0,
+       gpiomask:       0x00,
+       gpiomask2:      0x07ff,
+       muxsel:         { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03,
+                         0xd3, 0xb3, 0xc3, 0x63, 0x93, 0x53, 0x83, 0xa3 },
+       no_msp34xx:     1,
+       no_tda9875:     1,
+       tuner_type:     -1,
+       muxsel_hook:    rv605_muxsel,
+},{
+        name:           "Powercolor MTV878/ MTV878R/ MTV878F",
+        video_inputs:   3,
+        audio_inputs:   2, 
+        svhs:           2,
+        gpiomask:       0x1C800F,  // Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset
+        muxsel:         { 2, 1, 1, },
+        audiomux:       { 0, 1, 2, 2, 4 },
+        needs_tvaudio:  0,
+        tuner_type:     TUNER_PHILIPS_PAL,
+       pll:            PLL_28,
+       has_radio:      1,
+},{
+
+/* ---- card 0x4c ---------------------------------- */
+        /* Masaki Suzuki <masaki@btree.org> */
+        name:           "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)",
+        video_inputs:   3,
+        audio_inputs:   1,
+        tuner:          0,
+        svhs:           2,
+        gpiomask:       0x140007,
+        muxsel:         { 2, 3, 1, 1 },
+        audiomux:       { 0, 1, 2, 3, 4, 0 },
+        tuner_type:     TUNER_PHILIPS_NTSC,
+        audio_hook:     windvr_audio,
+},{
+        name:           "GrandTec Multi Capture Card (Bt878)",
+        video_inputs:   4,
+        audio_inputs:   0,
+        tuner:          -1,
+        svhs:           -1,
+        gpiomask:       0,
+        muxsel:         { 2, 3, 1, 0 },
+        audiomux:       { 0 },
+        needs_tvaudio:  0,
+        no_msp34xx:     1,
+        pll:            PLL_28,
+        tuner_type:     -1,
+}};
 
 const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard));
 
@@ -1195,12 +1278,58 @@ void __devinit bttv_idcard(struct bttv *btv)
  * (most) board specific initialisations goes here
  */
 
+static void flyvideo_gpio(struct bttv *btv)
+{
+       int gpio,outbits;
+       int tuner=-1,ttype;
+       
+       outbits = btread(BT848_GPIO_OUT_EN);
+       btwrite(0x00, BT848_GPIO_OUT_EN);
+       udelay(8);  // without this we would see the 0x1800 mask
+       gpio=btread(BT848_GPIO_DATA);
+       btwrite(outbits, BT848_GPIO_OUT_EN);
+       // all cards provide GPIO info, some have an additional eeprom
+       
+       // lowest 3 bytes are remote control codes (no handshake needed)
+       ttype=(gpio&0x0f0000)>>16;
+       switch(ttype) {
+       case 0: tuner=4; // None
+               break;
+       case 4: tuner=5; // Philips PAL
+               break;
+       case 6: tuner=37; // LG PAL (newer TAPC series)
+               break;
+       case 0xC: tuner=3; // Philips SECAM(+PAL)
+               break;
+       default:
+               printk(KERN_INFO "bttv%d: flyvideo_gpio: unknown tuner type.\n", btv->nr);
+       }
+       
+       printk(KERN_INFO "bttv%d: Flyvideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", 
+              btv->nr,
+              gpio&0x400000? "yes":"no",
+              gpio&0x800000? "yes":"no", tuner, gpio);
+       
+       btv->tuner_type = tuner;
+       btv->has_radio = gpio&0x400000? 1:0; 
+}
+
 int miro_tunermap[] = { 0,6,2,3,   4,5,6,0,  3,0,4,5,  5,2,16,1,
                        14,2,17,1, 4,1,4,3,  1,2,16,1, 4,4,4,4 };
 int miro_fmtuner[]  = { 0,0,0,0,   0,0,0,0,  0,0,0,0,  0,0,0,1,
                        1,1,1,1,   1,1,1,0,  0,0,0,0,  0,0,0,0 };
 
-void __devinit bttv_init_card(struct bttv *btv)
+/* initialization part one -- before registering i2c bus */
+void __devinit bttv_init_card1(struct bttv *btv)
+{
+        if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
+                boot_msp34xx(btv,5);
+       if (btv->type == BTTV_VOODOOTV_FM)
+               boot_msp34xx(btv,20);
+}
+
+/* initialization part one -- after registering i2c bus */
+void __devinit bttv_init_card2(struct bttv *btv)
 {
        /* miro/pinnacle */
         if (btv->type == BTTV_MIRO      ||
@@ -1246,6 +1375,15 @@ void __devinit bttv_init_card(struct bttv *btv)
 #endif
        }
 
+       if (btv->type == BTTV_FLYVIDEO_98    ||
+           btv->type == BTTV_FLYVIDEO       ||
+           btv->type == BTTV_TYPHOON_TVIEW  ||
+           btv->type == BTTV_CHRONOS_VS2    ||
+           btv->type == BTTV_FLYVIDEO_98FM  ||
+           btv->type == BTTV_FLYVIDEO2000   ||
+           btv->type == BTTV_FLYVIDEO98EZ)
+               flyvideo_gpio(btv);
+
         if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) {
                /* pick up some config infos from the eeprom */
                bttv_readee(btv,eeprom_data,0xa0);
@@ -1449,22 +1587,24 @@ static void __devinit hauppauge_eeprom(struct bttv *btv)
                       btv->tuner_type, radio ? "yes" : "no");
 }
 
-// AVermedia specific stuff...
-// from  bktr_card.c
+
+/* ----------------------------------------------------------------------- */
+/* AVermedia specific stuff, from  bktr_card.c                             */
+
 int tuner_0_table[] = {
         TUNER_PHILIPS_NTSC,  TUNER_PHILIPS_PAL,
         TUNER_PHILIPS_PAL,   TUNER_PHILIPS_PAL,
         TUNER_PHILIPS_PAL,   TUNER_PHILIPS_PAL,
         TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM,
         TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL};
-/*
+#if 0
 int tuner_0_fm_table[] = {
         PHILIPS_FR1236_NTSC,  PHILIPS_FR1216_PAL,
         PHILIPS_FR1216_PAL,   PHILIPS_FR1216_PAL,
         PHILIPS_FR1216_PAL,   PHILIPS_FR1216_PAL,
         PHILIPS_FR1236_SECAM, PHILIPS_FR1236_SECAM,
         PHILIPS_FR1236_SECAM, PHILIPS_FR1216_PAL};
-*/
+#endif
 
 int tuner_1_table[] = {
         TUNER_TEMIC_NTSC,  TUNER_TEMIC_PAL,
@@ -1475,11 +1615,12 @@ int tuner_1_table[] = {
 
 static void __devinit avermedia_eeprom(struct bttv *btv)
 {
-        int tuner_make,tuner_tv_fm,tuner_format,tuner=0;
+        int tuner_make,tuner_tv_fm,tuner_format,tuner=0,remote;
 
        tuner_make   = (eeprom_data[0x41] & 0x7);
         tuner_tv_fm  = (eeprom_data[0x41] & 0x18) >> 3;
         tuner_format = (eeprom_data[0x42] & 0xf0) >> 4;
+       remote       = (eeprom_data[0x42] & 0x01);
 
        if (tuner_make == 0 || tuner_make == 2)
                if(tuner_format <=9)
@@ -1492,11 +1633,32 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
                btv->nr,eeprom_data[0x41],eeprom_data[0x42]);
        if(tuner) {
                btv->tuner_type=tuner;
-               printk("%d\n",tuner);
+               printk("%d",tuner);
        } else
-               printk("Unknown type\n");
+               printk("Unknown type");
+       printk(" radio:%s remote control:%s\n",
+               tuner_tv_fm?"yes":"no",
+               remote?"yes":"no");
 }
 
+/* used on Voodoo TV/FM (Voodoo 200), S0 wired to 0x10000 */
+void bttv_tda9880_setnorm(struct bttv *btv, int norm)
+{
+       // fix up our card entry
+       if(norm==VIDEO_MODE_NTSC) {
+               bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[0]=0x957fff;
+               bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[4]=0x957fff;
+               dprintk("bttv_tda9880_setnorm to NTSC\n");
+       }
+       else {
+               bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[0]=0x947fff;
+                bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[4]=0x947fff;
+               dprintk("bttv_tda9880_setnorm to PAL\n");
+       }
+       // set GPIO according
+       btaor(bttv_tvcards[btv->type].audiomux[btv->audio],
+              ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+}
 
 
 /*
@@ -1506,7 +1668,7 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
  * Hauppauge:  pin  5
  * Voodoo:     pin 20
  */
-void __devinit bttv_boot_msp34xx(struct bttv *btv, int pin)
+static void __devinit boot_msp34xx(struct bttv *btv, int pin)
 {
        int mask = (1 << pin);
 
@@ -1918,10 +2080,116 @@ pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set)
                }
        } else {
                v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
-                         VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+                       VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+       }
+}
+
+/*
+ * Dariusz Kowalewski <darekk@automex.pl>
+ * sound control for FlyVideo 2000S (with tda9874 decoder) 
+ * based on pvbt878p9b_audio() - this is not tested, please fix!!!
+ */
+static void
+fv2000s_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+       unsigned int val = 0xffff;
+
+#if BTTV_VERSION_CODE > KERNEL_VERSION(0,8,0)
+       if (btv->radio_user)
+               return;
+#else
+       if (btv->radio)
+               return;
+#endif
+       if (set) {
+               if (v->mode & VIDEO_SOUND_MONO) {
+                       val = 0x0000;
+               }
+               if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2))
+                   || (v->mode & VIDEO_SOUND_STEREO)) {
+                       val = 0x1080; //-dk-???: 0x0880, 0x0080, 0x1800 ...
+               }
+               if (val != 0xffff) {
+                       btaor(val, ~0x1800, BT848_GPIO_DATA);
+                       if (bttv_gpio)
+                               bttv_gpio_tracking(btv,"fv2000s");
+               }
+       } else {
+               v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+                       VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
        }
 }
 
+/*
+ * sound control for Canopus WinDVR PCI
+ * Masaki Suzuki <masaki@btree.org>
+ */
+static void
+windvr_audio(struct bttv *btv, struct video_audio *v, int set)
+{
+        unsigned long val = 0;
+
+        if (set) {
+                if (v->mode & VIDEO_SOUND_MONO)
+                        val = 0x040000;
+                if (v->mode & VIDEO_SOUND_LANG1)
+                        val = 0;
+                if (v->mode & VIDEO_SOUND_LANG2)
+                        val = 0x100000;
+                if (v->mode & VIDEO_SOUND_STEREO)
+                        val = 0;
+                if (val) {
+                        btaor(val, ~0x140000, BT848_GPIO_DATA);
+                        if (bttv_gpio)
+                                bttv_gpio_tracking(btv,"windvr");
+                }
+        } else {
+                v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
+                          VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
+        }
+}
+
+/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
+ *
+ * This is needed because rv605 don't use a normal multiplex, but a crosspoint
+ * switch instead (CD22M3494E). This IC can have multiple active connections
+ * between Xn (input) and Yn (output) pins. We need to clear any existing
+ * connection prior to establish a new one, pulsing the STROBE pin.
+ *
+ * The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin.
+ * GPIO pins are wired as:
+ *  GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroler)
+ *  GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroler)
+ *  GPIO[7]   - DATA (xpoint)    - P1[7] (microcontroler)
+ *  GPIO[8]   -                  - P3[5] (microcontroler)
+ *  GPIO[9]   - RESET (xpoint)   - P3[6] (microcontroler)
+ *  GPIO[10]  - STROBE (xpoint)  - P3[7] (microcontroler)
+ *  GPINTR    -                  - P3[4] (microcontroler)
+ *
+ * The microcontroler is a 80C32 like. It should be possible to change xpoint
+ * configuration either directly (as we are doing) or using the microcontroler
+ * which is also wired to I2C interface. I have no further info on the
+ * microcontroler features, one would need to disassembly the firmware.
+ * note: the vendor refused to give any information on this product, all
+ *       that stuff was found using a multimeter! :)
+ */
+static void rv605_muxsel(struct bttv *btv, unsigned int input)
+{
+       /* reset all conections */
+       btaor(0x200,~0x200, BT848_GPIO_DATA);
+       mdelay(1);
+       btaor(0x000,~0x200, BT848_GPIO_DATA);
+       mdelay(1);
+
+       /* create a new conection */
+       btaor(0x080,~0x480, BT848_GPIO_DATA);
+       btaor(0x480,~0x480, BT848_GPIO_DATA);
+       mdelay(1);
+       btaor(0x080,~0x480, BT848_GPIO_DATA);
+       mdelay(1);
+}
+
+
 /* ----------------------------------------------------------------------- */
 /* motherboard chipset specific stuff                                      */
 
index e6d61605f86e68ff670fde518fe7ba72c8882d95..95bfb3066c2009f6fc247c8b9499c21b517c50cf 100644 (file)
@@ -1,20 +1,23 @@
 /*
     bttv - Bt848 frame grabber driver
-
-    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
-                           & Marcus Metzler (mocm@thp.uni-koeln.de)
-    (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
-
+    
+    Copyright (C) 1996,97,98 Ralph  Metzler <rjkm@thp.uni-koeln.de>
+                           & Marcus Metzler <mocm@thp.uni-koeln.de>
+    (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org>
+    
+    some v4l2 code lines are taken from Justin's bttv2 driver which is
+    (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za>
+    
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
     the Free Software Foundation; either version 2 of the License, or
     (at your option) any later version.
-
+    
     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.
-
+    
     You should have received a copy of the GNU General Public License
     along with this program; if not, write to the Free Software
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 #include <linux/errno.h>
 #include <linux/fs.h>
 #include <linux/kernel.h>
-#include <linux/major.h>
-#include <linux/slab.h>
-#include <linux/mm.h>
-#include <linux/poll.h>
-#include <linux/pci.h>
-#include <linux/signal.h>
-#include <asm/io.h>
-#include <linux/ioport.h>
-#include <asm/pgtable.h>
-#include <asm/page.h>
 #include <linux/sched.h>
-#include <linux/types.h>
-#include <linux/wrapper.h>
 #include <linux/interrupt.h>
-#include <linux/kmod.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
+#include <linux/kdev_t.h>
+
+#include <asm/io.h>
 
 #include "bttvp.h"
 #include "tuner.h"
 
-#define DEBUG(x)       /* Debug driver */
-#define MIN(a,b) (((a)>(b))?(b):(a))
-#define MAX(a,b) (((a)>(b))?(a):(b))
-
-static void bt848_set_risc_jmps(struct bttv *btv, int state);
+/* 2.4 / 2.5 driver compatibility stuff */
 
 int bttv_num;                  /* number of Bt848s in use */
 struct bttv bttvs[BTTV_MAX];
 
-/* configuration variables */
+unsigned int bttv_debug = 0;
+unsigned int bttv_verbose = 1;
+unsigned int bttv_gpio = 0;
+
+/* config variables */
 #if defined(__sparc__) || defined(__powerpc__) || defined(__hppa__)
 static unsigned int bigendian=1;
 #else
 static unsigned int bigendian=0;
 #endif
-static unsigned int radio[BTTV_MAX];
-static unsigned int fieldnr = 0;
+static unsigned int radio[4];
 static unsigned int irq_debug = 0;
-static unsigned int gbuffers = 2;
-static unsigned int gbufsize = BTTV_MAX_FBUF;
-static unsigned int combfilter = 0;
-static unsigned int lumafilter = 0;
+static unsigned int gbuffers = 8;
+static unsigned int gbufsize = 0x208000;
+static unsigned int fdsr = 0;
+static unsigned int latency = -1;
+
 static int video_nr = -1;
 static int radio_nr = -1;
 static int vbi_nr = -1;
-unsigned int bttv_debug = 0;
-unsigned int bttv_verbose = 1;
-unsigned int bttv_gpio = 0;
 
-/* insmod options */
+/* options */
+static unsigned int combfilter = 0;
+static unsigned int lumafilter = 0;
+static unsigned int automute   = 1;
+static unsigned int chroma_agc = 0;
+static unsigned int adc_crush  = 1;
+
+/* API features (turn on/off stuff for testing) */
+static unsigned int sloppy     = 0;
+static unsigned int mmap       = 1;
+#ifdef HAVE_V4L2
+static unsigned int v4l2       = 1;
+#endif
+
+
+/* insmod args */
 MODULE_PARM(radio,"1-4i");
 MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
 MODULE_PARM(bigendian,"i");
 MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
-MODULE_PARM(fieldnr,"i");
-MODULE_PARM_DESC(fieldnr,"count fields, default is 0 (no)");
 MODULE_PARM(bttv_verbose,"i");
 MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
 MODULE_PARM(bttv_gpio,"i");
@@ -92,17 +93,33 @@ MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
 MODULE_PARM(irq_debug,"i");
 MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
 MODULE_PARM(gbuffers,"i");
-MODULE_PARM_DESC(gbuffers,"number of capture buffers, default is 2 (64 max)");
+MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8");
 MODULE_PARM(gbufsize,"i");
 MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
-MODULE_PARM(combfilter,"i");
-MODULE_PARM(lumafilter,"i");
+MODULE_PARM(fdsr,"i");
+MODULE_PARM(latency,"i");
+MODULE_PARM_DESC(latency,"pci latency timer");
 
 MODULE_PARM(video_nr,"i");
 MODULE_PARM(radio_nr,"i");
 MODULE_PARM(vbi_nr,"i");
 
-MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards");
+MODULE_PARM(combfilter,"i");
+MODULE_PARM(lumafilter,"i");
+MODULE_PARM(automute,"i");
+MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)");
+MODULE_PARM(chroma_agc,"i");
+MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)");
+MODULE_PARM(adc_crush,"i");
+MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)");
+
+MODULE_PARM(sloppy,"i");
+MODULE_PARM(mmap,"i");
+#ifdef HAVE_V4L2
+MODULE_PARM(v4l2,"i");
+#endif
+
+MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards");
 MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
 MODULE_LICENSE("GPL");
 
@@ -112,200 +129,486 @@ static int __init p_radio(char *str) { return bttv_parse(str,BTTV_MAX,radio); }
 __setup("bttv.radio=", p_radio);
 #endif
 
-#define I2C_TIMING (0x7<<4)
-#define I2C_DELAY   10
-
-#define I2C_SET(CTRL,DATA) \
-    { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); }
-#define I2C_GET()   (btread(BT848_I2C)&1)
+#ifndef HAVE_V4L2
+/* some dummy defines to avoid cluttering up the source code with
+   a huge number of ifdef's for V4L2 */
+# define V4L2_STD_PAL          -1
+# define V4L2_STD_NTSC         -1
+# define V4L2_STD_SECAM        -1
+# define V4L2_STD_PAL_60       -1
+# define V4L2_STD_PAL_M        -1
+# define V4L2_STD_PAL_N        -1
+# define V4L2_STD_NTSC_N       -1
+# define V4L2_PIX_FMT_GREY     -1
+# define V4L2_PIX_FMT_HI240    -1
+# define V4L2_PIX_FMT_RGB555   -1
+# define V4L2_PIX_FMT_RGB555X  -1
+# define V4L2_PIX_FMT_RGB565   -1
+# define V4L2_PIX_FMT_RGB565X  -1
+# define V4L2_PIX_FMT_BGR24    -1
+# define V4L2_PIX_FMT_BGR32    -1
+# define V4L2_PIX_FMT_RGB32    -1
+# define V4L2_PIX_FMT_YUYV     -1
+# define V4L2_PIX_FMT_UYVY     -1
+# define V4L2_PIX_FMT_YUV422P  -1
+# define V4L2_PIX_FMT_YUV420   -1
+# define V4L2_PIX_FMT_YVU420   -1
+# define V4L2_PIX_FMT_YUV411P  -1
+# define V4L2_PIX_FMT_YUV410   -1
+# define V4L2_PIX_FMT_YVU410   -1
+# define V4L2_BUF_TYPE_CAPTURE -1
+# define V4L2_BUF_TYPE_VBI     -1
+# define BTTV_APIS "[v4l]"
+#else
+# define BTTV_APIS "[v4l/v4l2]"
+#endif
 
-#define BURSTOFFSET 76
-#define BTTV_ERRORS 5
+/* ----------------------------------------------------------------------- */
+/* static data                                                             */
 
+const struct bttv_tvnorm bttv_tvnorms[] = {
+       /* PAL-BDGHI */
+        { V4L2_STD_PAL, 35468950,
+          922, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+          1135, 186, 922, 0x20, 255},
+       /* NTSC */
+       { V4L2_STD_NTSC, 28636363,
+          754, 480,  910, 0x70, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+          910, 135, 754, 0x1a, 144},
+       /* SECAM */
+        { V4L2_STD_SECAM, 35468950,
+          922, 576, 1135, 0x7f, 0xa0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+          1135, 186, 922, 0x20, 255},
 
-/*******************************/
-/* Memory management functions */
-/*******************************/
+       /* these ones are bttv specific (for v4l1) */
+        /* PAL-NC */
+        { V4L2_STD_PAL_60, 28636363,
+          640, 576,  910, 0x7f, 0x72, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
+          780, 130, 734, 0x1a, 144},
+       /* PAL-M */
+       { V4L2_STD_PAL_M, 28636363, 
+          640, 480, 910, 0x70, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+         780, 135, 754, 0x1a, 144},
+       /* PAL-N */
+       { V4L2_STD_PAL_N, 35468950, 
+          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT0),
+         944, 186, 922, 0x20, 144},
+       /* NTSC-Japan */
+       { V4L2_STD_NTSC_N, 28636363,
+          640, 480,  910, 0x70, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
+         780, 135, 754, 0x16, 144},
+};
+const int BTTV_TVNORMS = (sizeof(bttv_tvnorms)/sizeof(struct bttv_tvnorm));
 
-#define MDEBUG(x)      do { } while(0)         /* Debug memory management */
+/* ----------------------------------------------------------------------- */
+/* bttv format list
+   packed pixel formats must come first */
+const struct bttv_format bttv_formats[] = {
+       {
+               name:     "8 bpp, gray",
+               palette:  VIDEO_PALETTE_GREY,
+               fourcc:   V4L2_PIX_FMT_GREY,
+               btformat: BT848_COLOR_FMT_Y8,
+               depth:    8,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "8 bpp, dithered color",
+               palette:  VIDEO_PALETTE_HI240,
+               fourcc:   V4L2_PIX_FMT_HI240,
+               btformat: BT848_COLOR_FMT_RGB8,
+               depth:    8,
+               flags:    FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER,
+       },{
+               name:     "15 bpp RGB, le",
+               palette:  VIDEO_PALETTE_RGB555,
+               fourcc:   V4L2_PIX_FMT_RGB555,
+               btformat: BT848_COLOR_FMT_RGB15,
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "15 bpp RGB, be",
+               palette:  -1,
+               fourcc:   V4L2_PIX_FMT_RGB555X,
+               btformat: BT848_COLOR_FMT_RGB15,
+               btswap:   0x03, /* byteswap */
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "16 bpp RGB, le",
+               palette:  VIDEO_PALETTE_RGB565,
+               fourcc:   V4L2_PIX_FMT_RGB565,
+               btformat: BT848_COLOR_FMT_RGB16,
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "16 bpp RGB, be",
+               palette:  -1,
+               fourcc:   V4L2_PIX_FMT_RGB565X,
+               btformat: BT848_COLOR_FMT_RGB16,
+               btswap:   0x03, /* byteswap */
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "24 bpp RGB, le",
+               palette:  VIDEO_PALETTE_RGB24,
+               fourcc:   V4L2_PIX_FMT_BGR24,
+               btformat: BT848_COLOR_FMT_RGB24,
+               depth:    24,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "32 bpp RGB, le",
+               palette:  VIDEO_PALETTE_RGB32,
+               fourcc:   V4L2_PIX_FMT_BGR32,
+               btformat: BT848_COLOR_FMT_RGB32,
+               depth:    32,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "32 bpp RGB, be",
+               palette:  -1,
+               fourcc:   V4L2_PIX_FMT_RGB32,
+               btformat: BT848_COLOR_FMT_RGB32,
+               btswap:   0x0f, /* byte+word swap */
+               depth:    32,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "4:2:2, packed, YUYV",
+               palette:  VIDEO_PALETTE_YUV422,
+               fourcc:   V4L2_PIX_FMT_YUYV,
+               btformat: BT848_COLOR_FMT_YUY2,
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "4:2:2, packed, YUYV",
+               palette:  VIDEO_PALETTE_YUYV,
+               fourcc:   V4L2_PIX_FMT_YUYV,
+               btformat: BT848_COLOR_FMT_YUY2,
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "4:2:2, packed, UYVY",
+               palette:  VIDEO_PALETTE_UYVY,
+               fourcc:   V4L2_PIX_FMT_UYVY,
+               btformat: BT848_COLOR_FMT_YUY2,
+               btswap:   0x03, /* byteswap */
+               depth:    16,
+               flags:    FORMAT_FLAGS_PACKED,
+       },{
+               name:     "4:2:2, planar, Y-Cb-Cr",
+               palette:  VIDEO_PALETTE_YUV422P,
+               fourcc:   V4L2_PIX_FMT_YUV422P,
+               btformat: BT848_COLOR_FMT_YCrCb422,
+               depth:    16,
+               flags:    FORMAT_FLAGS_PLANAR,
+               hshift:   1,
+               vshift:   0,
+       },{
+               name:     "4:2:0, planar, Y-Cb-Cr",
+               palette:  VIDEO_PALETTE_YUV420P,
+               fourcc:   V4L2_PIX_FMT_YUV420,
+               btformat: BT848_COLOR_FMT_YCrCb422,
+               depth:    12,
+               flags:    FORMAT_FLAGS_PLANAR,
+               hshift:   1,
+               vshift:   1,
+       },{
+               name:     "4:2:0, planar, Y-Cr-Cb",
+               palette:  -1,
+               fourcc:   V4L2_PIX_FMT_YVU420,
+               btformat: BT848_COLOR_FMT_YCrCb422,
+               depth:    12,
+               flags:    FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+               hshift:   1,
+               vshift:   1,
+       },{
+               name:     "4:1:1, planar, Y-Cb-Cr",
+               palette:  VIDEO_PALETTE_YUV411P,
+               fourcc:   V4L2_PIX_FMT_YUV411P,
+               btformat: BT848_COLOR_FMT_YCrCb411,
+               depth:    12,
+               flags:    FORMAT_FLAGS_PLANAR,
+               hshift:   2,
+               vshift:   0,
+       },{
+               name:     "4:1:0, planar, Y-Cb-Cr",
+               palette:  VIDEO_PALETTE_YUV410P,
+               fourcc:   V4L2_PIX_FMT_YUV410,
+               btformat: BT848_COLOR_FMT_YCrCb411,
+               depth:    9,
+               flags:    FORMAT_FLAGS_PLANAR,
+               hshift:   2,
+               vshift:   2,
+       },{
+               name:     "4:1:0, planar, Y-Cr-Cb",
+               palette:  -1,
+               fourcc:   V4L2_PIX_FMT_YVU410,
+               btformat: BT848_COLOR_FMT_YCrCb411,
+               depth:    9,
+               flags:    FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb,
+               hshift:   2,
+               vshift:   2,
+       },{
+               name:     "raw scanlines",
+               palette:  VIDEO_PALETTE_RAW,
+               fourcc:   -1,
+               btformat: BT848_COLOR_FMT_RAW,
+               depth:    8,
+               flags:    FORMAT_FLAGS_RAW,
+       }
+};
+const int BTTV_FORMATS = (sizeof(bttv_formats)/sizeof(struct bttv_format));
 
-/* [DaveM] I've recoded most of this so that:
- * 1) It's easier to tell what is happening
- * 2) It's more portable, especially for translating things
- *    out of vmalloc mapped areas in the kernel.
- * 3) Less unnecessary translations happen.
- *
- * The code used to assume that the kernel vmalloc mappings
- * existed in the page tables of every process, this is simply
- * not guarenteed.  We now use pgd_offset_k which is the
- * defined way to get at the kernel page tables.
- */
+/* ----------------------------------------------------------------------- */
 
+#ifdef HAVE_V4L2
+#define V4L2_CID_PRIVATE_CHROMA_AGC  (V4L2_CID_PRIVATE_BASE + 0)
+#define V4L2_CID_PRIVATE_COMBFILTER  (V4L2_CID_PRIVATE_BASE + 1)
+#define V4L2_CID_PRIVATE_AUTOMUTE    (V4L2_CID_PRIVATE_BASE + 2)
+#define V4L2_CID_PRIVATE_LUMAFILTER  (V4L2_CID_PRIVATE_BASE + 3)
+#define V4L2_CID_PRIVATE_AGC_CRUSH   (V4L2_CID_PRIVATE_BASE + 4)
+#define V4L2_CID_PRIVATE_LASTP1      (V4L2_CID_PRIVATE_BASE + 5)
+
+static const struct v4l2_queryctrl no_ctl = {
+       name:  "42",
+       flags: V4L2_CTRL_FLAG_DISABLED,
+};
+static const struct v4l2_queryctrl bttv_ctls[] = {
+       /* --- video --- */
+       {
+               id:            V4L2_CID_BRIGHTNESS,
+               name:          "Brightness",
+               minimum:       0,
+               maximum:       65535,
+               step:          256,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_VIDEO,
+               group:         "Video",
+       },{
+               id:            V4L2_CID_CONTRAST,
+               name:          "Contrast",
+               minimum:       0,
+               maximum:       65535,
+               step:          128,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_VIDEO,
+               group:         "Video",
+       },{
+               id:            V4L2_CID_SATURATION,
+               name:          "Saturation",
+               minimum:       0,
+               maximum:       65535,
+               step:          128,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_VIDEO,
+               group:         "Video",
+       },{
+               id:            V4L2_CID_HUE,
+               name:          "Hue",
+               minimum:       0,
+               maximum:       65535,
+               step:          256,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_VIDEO,
+               group:         "Video",
+       },
+       /* --- audio --- */
+       {
+               id:            V4L2_CID_AUDIO_MUTE,
+               name:          "Mute",
+               minimum:       0,
+               maximum:       1,
+               type:          V4L2_CTRL_TYPE_BOOLEAN,
+               category:      V4L2_CTRL_CAT_AUDIO,
+               group:         "Audio",
+       },{
+               id:            V4L2_CID_AUDIO_VOLUME,
+               name:          "Volume",
+               minimum:       0,
+               maximum:       65535,
+               step:          65535/100,
+               default_value: 65535,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_AUDIO,
+               group:         "Audio",
+       },{
+               id:            V4L2_CID_AUDIO_BALANCE,
+               name:          "Balance",
+               minimum:       0,
+               maximum:       65535,
+               step:          65535/100,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_AUDIO,
+               group:         "Audio",
+       },{
+               id:            V4L2_CID_AUDIO_BASS,
+               name:          "Bass",
+               minimum:       0,
+               maximum:       65535,
+               step:          65535/100,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_AUDIO,
+               group:         "Audio",
+       },{
+               id:            V4L2_CID_AUDIO_TREBLE,
+               name:          "Treble",
+               minimum:       0,
+               maximum:       65535,
+               step:          65535/100,
+               default_value: 32768,
+               type:          V4L2_CTRL_TYPE_INTEGER,
+               category:      V4L2_CTRL_CAT_AUDIO,
+               group:         "Audio",
+       },
+       /* --- private --- */
+       {
+               id:            V4L2_CID_PRIVATE_CHROMA_AGC,
+               name:          "chroma agc",
+               minimum:       0,
+               maximum:       1,
+               type:          V4L2_CTRL_TYPE_BOOLEAN,
+               group:         "Private",
+       },{
+               id:            V4L2_CID_PRIVATE_COMBFILTER,
+               name:          "combfilter",
+               minimum:       0,
+               maximum:       1,
+               type:          V4L2_CTRL_TYPE_BOOLEAN,
+               group:         "Private",
+       },{
+               id:            V4L2_CID_PRIVATE_AUTOMUTE,
+               name:          "automute",
+               minimum:       0,
+               maximum:       1,
+               type:          V4L2_CTRL_TYPE_BOOLEAN,
+               group:         "Private",
+       },{
+               id:            V4L2_CID_PRIVATE_LUMAFILTER,
+               name:          "luma decimation filter",
+               minimum:       0,
+               maximum:       1,
+               type:          V4L2_CTRL_TYPE_BOOLEAN,
+               group:         "Private",
+       },{
+               id:            V4L2_CID_PRIVATE_AGC_CRUSH,
+               name:          "agc crush",
+               minimum:       0,
+               maximum:       1,
+               type:          V4L2_CTRL_TYPE_BOOLEAN,
+               group:         "Private",
+       }
+};
+const int BTTV_CTLS = (sizeof(bttv_ctls)/sizeof(struct v4l2_queryctrl));
+#endif /* HAVE_V4L2 */
 
-/*
- * Take a vmalloc address, and turn it into
- * the aliased kernel virtual address..
- *
- * CAREFUL! Anybody who does this gets to sit
- * in their own cr*p when it comes to virtual
- * cache aliases. It's _your_ problem.
- *
- * Also, note how it only works within one page.
- * If you're doing page-crossing stuff, you're on
- * your own.
- *
- * THIS IS BROKEN CODE! You shouldn't do things
- * like this.
- */
-static inline void *vmalloc_to_virt(void *addr)
-{
-       struct page *page = vmalloc_to_page(addr);
-       return page_address(page) + (~PAGE_MASK & (unsigned long)addr);
-}
+/* ----------------------------------------------------------------------- */
+/* resource management                                                     */
 
-static inline unsigned long kvirt_to_bus(unsigned long addr) 
+static
+int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit)
 {
-       unsigned long ret;
-       ret = virt_to_bus(vmalloc_to_virt((void *)addr));
-        MDEBUG(printk("kv2b(%lx-->%lx)", addr, ret));
-        return ret;
+       if (fh->resources & bit)
+               /* have it already allocated */
+               return 1;
+
+       /* is it free? */
+       down(&btv->reslock);
+       if (btv->resources & bit) {
+               /* no, someone else uses it */
+               up(&btv->reslock);
+               return 0;
+       }
+       /* it's free, grab it */
+       fh->resources  |= bit;
+       btv->resources |= bit;
+       up(&btv->reslock);
+       return 1;
 }
 
-/* Here we want the physical address of the memory.
- * This is used when initializing the contents of the area.
- */
-static inline unsigned long kvirt_to_pa(unsigned long addr) 
+static
+int check_btres(struct bttv_fh *fh, int bit)
 {
-        unsigned long ret;
-       ret = virt_to_phys(vmalloc_to_virt((void *)addr));
-        MDEBUG(printk("kv2pa(%lx-->%lx)", addr, ret));
-        return ret;
+       return (fh->resources & bit);
 }
 
-static void * rvmalloc(unsigned long size)
+static
+int locked_btres(struct bttv *btv, int bit)
 {
-       void * mem;
-       unsigned long adr;
-
-       size=PAGE_ALIGN(size);
-       mem=vmalloc_32(size);
-       if (NULL == mem)
-               printk(KERN_INFO "bttv: vmalloc_32(%lu) failed\n",size);
-       else {
-               /* Clear the ram out, no junk to the user */
-               memset(mem, 0, size);
-               adr=(unsigned long) mem;
-               while (size > 0) 
-                {
-                       mem_map_reserve(vmalloc_to_page((void *)adr));
-                       adr+=PAGE_SIZE;
-                       size-=PAGE_SIZE;
-               }
-       }
-       return mem;
+       return (btv->resources & bit);
 }
 
-static void rvfree(void * mem, unsigned long size)
+static
+void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits)
 {
-        unsigned long adr;
-        
-       if (mem) 
-       {
-               adr=(unsigned long) mem;
-               while ((long) size > 0)
-                {
-                       mem_map_unreserve(vmalloc_to_page((void *)adr));
-                       adr+=PAGE_SIZE;
-                       size-=PAGE_SIZE;
-               }
-               vfree(mem);
+#if 1 /* DEBUG */
+       if ((fh->resources & bits) != bits) {
+               /* trying to free ressources not allocated by us ... */
+               printk("bttv: BUG! (btres)\n");
        }
+#endif
+       down(&btv->reslock);
+       fh->resources  &= ~bits;
+       btv->resources &= ~bits;
+       up(&btv->reslock);
 }
 
-
-
+/* ----------------------------------------------------------------------- */
 /*
- *     Create the giant waste of buffer space we need for now
- *     until we get DMA to user space sorted out (probably 2.3.x)
+ * sanity check for video framebuffer address ranges (overlay).
+ * let's see if that address range actually belongs to some
+ * pci display adapter.
  *
- *     We only create this as and when someone uses mmap
+ * FIXME: stuff isn't portable.  It's also a v4l API bug, pass a
+ * physical address in VIDIOCSFBUF isn't portable too ...
  */
-static int fbuffer_alloc(struct bttv *btv)
-{
-       if(!btv->fbuffer)
-               btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
-       else
-               printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n",
-                       btv->nr);
-       if(!btv->fbuffer)
-               return -ENOBUFS;
-       return 0;
-}
-
-/* ----------------------------------------------------------------------- */
-
-void bttv_gpio_tracking(struct bttv *btv, char *comment)
-{
-       unsigned int outbits, data;
-       outbits = btread(BT848_GPIO_OUT_EN);
-       data    = btread(BT848_GPIO_DATA);
-       printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
-              btv->nr,outbits,data & outbits, data & ~outbits, comment);
-}
 
-static char *audio_modes[] = { "audio: tuner", "audio: radio", "audio: extern",
-                              "audio: intern", "audio: off" };
-
-static void audio(struct bttv *btv, int mode, int no_irq_context)
+static int
+find_videomem(unsigned long from, unsigned long to)
 {
-       btaor(bttv_tvcards[btv->type].gpiomask, ~bttv_tvcards[btv->type].gpiomask,
-              BT848_GPIO_OUT_EN);
-
-       switch (mode)
-       {
-               case AUDIO_MUTE:
-                        btv->audio|=AUDIO_MUTE;
-                       break;
-               case AUDIO_UNMUTE:
-                       btv->audio&=~AUDIO_MUTE;
-                       mode=btv->audio;
-                       break;
-               case AUDIO_OFF:
-                       mode=AUDIO_OFF;
-                       break;
-               case AUDIO_ON:
-                       mode=btv->audio;
-                       break;
-               default:
-                       btv->audio&=AUDIO_MUTE;
-                       btv->audio|=mode;
-                       break;
+#if PCI_DMA_BUS_IS_PHYS
+       struct pci_dev *dev = NULL;
+       int i,match,found;
+
+       found = 0;
+       dprintk(KERN_DEBUG "bttv: checking video framebuffer address"
+               " (%lx-%lx)\n",from,to);
+        pci_for_each_dev(dev) {
+               if (dev->class != PCI_CLASS_NOT_DEFINED_VGA &&
+                   dev->class >> 16 != PCI_BASE_CLASS_DISPLAY)
+                       continue;
+               dprintk(KERN_DEBUG
+                       "  pci display adapter %04x:%04x at %02x:%02x.%x\n",
+                       dev->vendor,dev->device,dev->bus->number,
+                       PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
+               for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
+                       if (!(dev->resource[i].flags & IORESOURCE_MEM))
+                               continue;
+                       if (dev->resource[i].flags & IORESOURCE_READONLY)
+                               continue;
+                       match = (from >= dev->resource[i].start) &&
+                               (to-1 <= dev->resource[i].end);
+                       if (match)
+                               found = 1;
+                       dprintk(KERN_DEBUG "    memory at %08lx-%08lx%s\n",
+                               dev->resource[i].start,
+                               dev->resource[i].end,
+                               match ? "  (check passed)" : "");
+               }
        }
-        /* if audio mute or not in H-lock, turn audio off */
-       if ((btv->audio&AUDIO_MUTE))
-               mode=AUDIO_OFF;
-        if ((mode == AUDIO_TUNER) && (btv->radio))
-               mode = AUDIO_RADIO;
-       btaor(bttv_tvcards[btv->type].audiomux[mode],
-              ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
-       if (bttv_gpio)
-               bttv_gpio_tracking(btv,audio_modes[mode]);
-       if (no_irq_context)
-               bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode));
-}
-
-
-extern inline void bt848_dma(struct bttv *btv, uint state)
-{
-       if (state)
-               btor(3, BT848_GPIO_DMA_CTL);
-       else
-               btand(~3, BT848_GPIO_DMA_CTL);
+       return found;
+#else
+       /* Hmm, the physical address passed to us is probably bogous */
+       dprintk(KERN_DEBUG "bttv: no overlay for this arch, sorry\n");
+       return 0;
+#endif
 }
 
-
-/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/
+/* ----------------------------------------------------------------------- */
+/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC          */
 
 /* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C 
    PLL_X = Reference pre-divider (0=1, 1=2) 
@@ -334,7 +637,6 @@ static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
         fout=(fout%fin)*256;
         fl=fout/fin;
 
-        /*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/
         btwrite(fl, BT848_PLL_F_LO);
         btwrite(fh, BT848_PLL_F_HI);
         btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
@@ -343,2301 +645,2378 @@ static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
 static int set_pll(struct bttv *btv)
 {
         int i;
-       unsigned long tv;
 
         if (!btv->pll.pll_crystal)
                 return 0;
 
         if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
                 /* no PLL needed */
-                if (btv->pll.pll_current == 0) {
-                        /* printk ("bttv%d: PLL: is off\n",btv->nr); */
+                if (btv->pll.pll_current == 0)
                         return 0;
-                }
-               if (bttv_verbose)
-                       printk ("bttv%d: PLL: switching off\n",btv->nr);
+               vprintk("bttv%d: PLL: switching off\n",btv->nr);
                 btwrite(0x00,BT848_TGCTRL);
                 btwrite(0x00,BT848_PLL_XCI);
                 btv->pll.pll_current = 0;
                 return 0;
         }
 
-        if (btv->pll.pll_ofreq == btv->pll.pll_current) {
-                /* printk("bttv%d: PLL: no change required\n",btv->nr); */
-                return 1;
-        }
-
-        if (bttv_verbose)
-               printk("bttv%d: PLL: %d => %d ... ",btv->nr,
-                      btv->pll.pll_ifreq, btv->pll.pll_ofreq);
+        if (btv->pll.pll_ofreq == btv->pll.pll_current)
+                return 0;
 
+       vprintk("bttv%d: PLL: %d => %d ",btv->nr,
+               btv->pll.pll_ifreq, btv->pll.pll_ofreq);
        set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
 
-       /*  Let other people run while the PLL stabilizes */
-       tv=jiffies+HZ/10;       /* .1 seconds */
-       do
-       {
-               schedule();
-       }
-       while(time_before(jiffies,tv));
-
-        for (i=0; i<100; i++) 
-        {
-                if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK))
-                        btwrite(0,BT848_DSTATUS);
-                else
-                {
+        for (i=0; i<10; i++) {
+               /*  Let other people run while the PLL stabilizes */
+               vprintk(".");
+               current->state = TASK_INTERRUPTIBLE;
+               schedule_timeout(HZ/10);
+               
+                if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) {
+                       btwrite(0,BT848_DSTATUS);
+                } else {
                         btwrite(0x08,BT848_TGCTRL);
                         btv->pll.pll_current = btv->pll.pll_ofreq;
-                       if (bttv_verbose)
-                               printk("ok\n");
-                        return 1;
+                       vprintk(" ok\n");
+                        return 0;
                 }
-                mdelay(10);
         }
-        btv->pll.pll_current = 0;
-       if (bttv_verbose)
-               printk("oops\n");
+        btv->pll.pll_current = -1;
+       vprintk("failed\n");
         return -1;
 }
 
-static void bt848_muxsel(struct bttv *btv, unsigned int input)
+/* ----------------------------------------------------------------------- */
+
+static void bt848_bright(struct bttv *btv, int bright)
 {
+       int value;
 
-#if 0 /* seems no card uses this ... */
-       btaor(bttv_tvcards[btv->type].gpiomask2,~bttv_tvcards[btv->type].gpiomask2,
-              BT848_GPIO_OUT_EN);
-#endif
+       btv->bright = bright;
+
+       /* We want -128 to 127 we get 0-65535 */
+       value = (bright >> 8) - 128;
+       btwrite(value & 0xff, BT848_BRIGHT);
+}
+
+static void bt848_hue(struct bttv *btv, int hue)
+{
+       int value;
+       
+       btv->hue = hue;
+
+       /* -128 to 127 */
+       value = (hue >> 8) - 128;
+        btwrite(value & 0xff, BT848_HUE);
+}
+
+static void bt848_contrast(struct bttv *btv, int cont)
+{
+       int value,hibit;
+       
+       btv->contrast = cont;
+       
+       /* 0-511 */
+       value = (cont  >> 7);
+       hibit = (value >> 6) & 4;
+        btwrite(value & 0xff, BT848_CONTRAST_LO);
+        btaor(hibit, ~4, BT848_E_CONTROL);
+        btaor(hibit, ~4, BT848_O_CONTROL);
+}
+
+static void bt848_sat(struct bttv *btv, int color)
+{
+       int val_u,val_v,hibits;
+       
+       btv->saturation = color;
+
+       /* 0-511 for the color */
+       val_u   = color >> 7;
+       val_v   = ((color>>7)*180L)/254;
+        hibits  = (val_u >> 7) & 2;
+       hibits |= (val_v >> 8) & 1;
+        btwrite(val_u & 0xff, BT848_SAT_U_LO);
+        btwrite(val_v & 0xff, BT848_SAT_V_LO);
+        btaor(hibits, ~3, BT848_E_CONTROL);
+        btaor(hibits, ~3, BT848_O_CONTROL);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int
+video_mux(struct bttv *btv, unsigned int input)
+{
+       int mux,mask2;
+
+       if (input >= bttv_tvcards[btv->type].video_inputs)
+               return -EINVAL;
+
+        /* needed by RemoteVideo MX */
+       mask2 = bttv_tvcards[btv->type].gpiomask2;
+       if (mask2)
+               btaor(mask2,~mask2,BT848_GPIO_OUT_EN);
 
+#if 0
        /* This seems to get rid of some synchronization problems */
        btand(~(3<<5), BT848_IFORM);
-       mdelay(10); 
+       schedule_timeout(HZ/10);
+#endif
         
-       input %= bttv_tvcards[btv->type].video_inputs;
-       if (input==bttv_tvcards[btv->type].svhs) 
-       {
+       if (input==bttv_tvcards[btv->type].svhs)  {
                btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
                btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
-       }
-       else
-       {
+       } else {
                btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
                btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
        }
-       btaor((bttv_tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM);
-       audio(btv, (input!=bttv_tvcards[btv->type].tuner) ? 
-              AUDIO_EXTERN : AUDIO_TUNER, 1);
+       mux = bttv_tvcards[btv->type].muxsel[input] & 3;
+       btaor(mux<<5, ~(3<<5), BT848_IFORM);
+       dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
+               btv->nr,input,mux);
 
-#if 0 /* seems no card uses this ... */
-       btaor(bttv_tvcards[btv->type].muxsel[input]>>4,
-               ~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_DATA);
-       if (bttv_gpio)
-               bttv_gpio_tracking(btv,"muxsel");
-#endif
+       /* card specific hook */
+       if(bttv_tvcards[btv->type].muxsel_hook)
+               bttv_tvcards[btv->type].muxsel_hook (btv, input);
+       return 0;
 }
 
-
-struct tvnorm 
-{
-        u32 Fsc;
-        u16 swidth, sheight; /* scaled standard width, height */
-       u16 totalwidth;
-       u8 adelay, bdelay, iform;
-       u32 scaledtwidth;
-       u16 hdelayx1, hactivex1;
-       u16 vdelay;
-        u8 vbipack;
+static char *audio_modes[] = {
+       "audio: tuner", "audio: radio", "audio: extern",
+       "audio: intern", "audio: off"
 };
 
-static struct tvnorm tvnorms[] = {
-       /* PAL-BDGHI */
-        /* max. active video is actually 922, but 924 is divisible by 4 and 3! */
-       /* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
-        { 35468950,
-          924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
-          1135, 186, 924,
-#ifdef VIDEODAT_HACK
-         VBI_MAXLINES*2,
-#else
-         0x20,
-#endif
-         255},
-
-       /* NTSC */
-       { 28636363,
-          768, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
-          910, 128, 910, 0x1a, 144},
-#if 0
-       /* SECAM EAST */
-       { 35468950, 
-          768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
-         944, 186, 922, 0x20, 255},
-#else
-       /* SECAM L */
-        { 35468950,
-          924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
-          1135, 186, 922, 0x20, 255},
-#endif
-        /* PAL-NC */
-        { 28636363,
-          640, 576,  910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
-          780, 130, 734, 0x1a, 144},
-       /* PAL-M */
-       { 28636363, 
-          640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
-         780, 135, 754, 0x1a, 144},
-       /* PAL-N */
-       { 35468950, 
-          768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
-         944, 186, 922, 0x20, 144},
-       /* NTSC-Japan */
-       { 28636363,
-          640, 480,  910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
-         780, 135, 754, 0x16, 144},
-};
-#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
-#define VBI_SPL 2044
+static int
+audio_mux(struct bttv *btv, int mode)
+{
+       int val,mux;
+       
+       btaor(bttv_tvcards[btv->type].gpiomask,
+             ~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN);
 
-/* RISC command to write one VBI data line */
-#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL
+       switch (mode) {
+       case AUDIO_MUTE:
+               btv->audio |= AUDIO_MUTE;
+               break;
+       case AUDIO_UNMUTE:
+               btv->audio &= ~AUDIO_MUTE;
+               break;
+       case AUDIO_TUNER:
+       case AUDIO_RADIO:
+       case AUDIO_EXTERN:
+       case AUDIO_INTERN:
+               btv->audio &= AUDIO_MUTE;
+               btv->audio |= mode;
+       }
+       dprintk("bttv%d: audio mux: mode=%d audio=%d\n",
+               btv->nr,mode,btv->audio);
+
+       mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
+       val = bttv_tvcards[btv->type].audiomux[mux];
+       btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv,audio_modes[mux]);
+       if (!in_interrupt())
+               bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mux));
+       return 0;
+}
 
-static void make_vbitab(struct bttv *btv)
+static void
+i2c_vidiocschan(struct bttv *btv)
 {
-       int i;
-       unsigned int *po=(unsigned int *) btv->vbi_odd;
-       unsigned int *pe=(unsigned int *) btv->vbi_even;
-  
-       if (bttv_debug > 1)
-               printk("bttv%d: vbi1: po=%08lx pe=%08lx\n",
-                      btv->nr,virt_to_bus(po), virt_to_bus(pe));
-        
-       *(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0;
-       for (i=0; i<VBI_MAXLINES; i++) 
-       {
-               *(po++)=cpu_to_le32(VBI_RISC);
-               *(po++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048));
-       }
-       *(po++)=cpu_to_le32(BT848_RISC_JUMP);
-       *(po++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+4));
+       struct video_channel c;
 
-       *(pe++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(pe++)=0;
-       for (i=VBI_MAXLINES; i<VBI_MAXLINES*2; i++) 
-       {
-               *(pe++)=cpu_to_le32(VBI_RISC);
-               *(pe++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048));
-       }
-       *(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16));
-       *(pe++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
-
-       if (bttv_debug > 1)
-               printk("bttv%d: vbi2: po=%08lx pe=%08lx\n",
-                      btv->nr,virt_to_bus(po), virt_to_bus(pe));
+       memset(&c,0,sizeof(c));
+       c.norm    = btv->tvnorm;
+       c.channel = btv->input;
+       bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
 }
 
-static int fmtbppx2[16] = {
-        8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 
-};
+static int
+set_tvnorm(struct bttv *btv, unsigned int norm)
+{
+       const struct bttv_tvnorm *tvnorm;
 
-static int palette2fmt[] = {
-       0,
-       BT848_COLOR_FMT_Y8,
-       BT848_COLOR_FMT_RGB8,
-       BT848_COLOR_FMT_RGB16,
-       BT848_COLOR_FMT_RGB24,
-       BT848_COLOR_FMT_RGB32,
-       BT848_COLOR_FMT_RGB15,
-       BT848_COLOR_FMT_YUY2,
-       BT848_COLOR_FMT_YUY2,
-       -1,
-       -1,
-       -1,
-       BT848_COLOR_FMT_RAW,
-       BT848_COLOR_FMT_YCrCb422,
-       BT848_COLOR_FMT_YCrCb411,
-       BT848_COLOR_FMT_YCrCb422,
-       BT848_COLOR_FMT_YCrCb411,
-};
-#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int))
+       if (norm < 0 || norm >= BTTV_TVNORMS)
+               return -EINVAL;
 
-static int make_rawrisctab(struct bttv *btv, unsigned int *ro,
-                            unsigned int *re, unsigned int *vbuf)
-{
-        unsigned long line;
-       unsigned long bpl=1024;         /* bytes per line */
-       unsigned long vadr=(unsigned long) vbuf;
-
-       *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); 
-        *(ro++)=cpu_to_le32(0);
-       *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
-        *(re++)=cpu_to_le32(0);
-  
-        /* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY
-           is 2 and without separate VBI grabbing.
-           We'll have to handle this inside the IRQ handler ... */
-
-       for (line=0; line < 640; line++)
-       {
-                *(ro++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
-                *(ro++)=cpu_to_le32(kvirt_to_bus(vadr));
-                *(re++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
-                *(re++)=cpu_to_le32(kvirt_to_bus(vadr+gbufsize/2));
-                vadr+=bpl;
-       }
-       
-       *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
-       *(ro++)=cpu_to_le32(btv->bus_vbi_even);
-       *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
-       *(re++)=cpu_to_le32(btv->bus_vbi_odd);
+       btv->tvnorm = norm;
+       tvnorm = &bttv_tvnorms[norm];
+
+       btwrite(tvnorm->adelay, BT848_ADELAY);
+       btwrite(tvnorm->bdelay, BT848_BDELAY);
+       btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH),
+             BT848_IFORM);
+       btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE);
+       btwrite(1, BT848_VBI_PACK_DEL);
        
+       btv->pll.pll_ofreq = tvnorm->Fsc;
+       set_pll(btv);
        return 0;
 }
 
-static int  make_prisctab(struct bttv *btv, unsigned int *ro,
-                          unsigned int *re, 
-                          unsigned int *vbuf, unsigned short width,
-                          unsigned short height, unsigned short fmt)
+static void
+set_input(struct bttv *btv, unsigned int input)
 {
-        unsigned long line, lmask;
-       unsigned long bl, blcr, blcb, rcmd;
-       unsigned long todo;
-       unsigned int **rp;
-       int inter;
-       unsigned long cbadr, cradr;
-       unsigned long vadr=(unsigned long) vbuf;
-       int shift, csize;       
-
-       if (bttv_debug > 1)
-               printk("bttv%d: prisc1: ro=%08lx re=%08lx\n",
-                      btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
-       switch(fmt)
-       {
-        case VIDEO_PALETTE_YUV422P:
-                csize=(width*height)>>1;
-                shift=1;
-                lmask=0;
-                break;
-                
-        case VIDEO_PALETTE_YUV411P:
-                csize=(width*height)>>2;
-                shift=2;
-                lmask=0;
-                break;
-                                       
-        case VIDEO_PALETTE_YUV420P:
-                csize=(width*height)>>2;
-                shift=1;
-                lmask=1;
-                break;
-                
-        case VIDEO_PALETTE_YUV410P:
-                csize=(width*height)>>4;
-                shift=2;
-                lmask=3;
-                break;
-                
-        default:
-                return -1;
-       }
-       cbadr=vadr+(width*height);
-       cradr=cbadr+csize;
-       inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
-       
-       *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
-        *(ro++)=0;
-       *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
-        *(re++)=0;
-  
-       for (line=0; line < (height<<(1^inter)); line++)
-       {
-               if(line==height)
-               {
-                       vadr+=csize<<1;
-                       cbadr=vadr+(width*height);
-                       cradr=cbadr+csize;
-               }
-               if (inter) 
-                       rp= (line&1) ? &re : &ro;
-               else 
-                       rp= (line>=height) ? &ro : &re; 
-                       
-
-               if(line&lmask)
-                       rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL;
-               else
-                       rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL;
-
-               todo=width;
-               while(todo)
-               {
-                 bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
-                 blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<<shift;
-                blcb=(PAGE_SIZE-((PAGE_SIZE-1)&cbadr))<<shift;
-                bl=(blcr<bl) ? blcr : bl;
-                bl=(blcb<bl) ? blcb : bl;
-                bl=(bl>todo) ? todo : bl;
-                blcr=bl>>shift;
-                blcb=blcr;
-                /* bl now containts the longest row that can be written */
-                todo-=bl;
-                if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */
-                
-                *((*rp)++)=cpu_to_le32(rcmd|bl);
-                *((*rp)++)=cpu_to_le32(blcb|(blcr<<16));
-                *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
-                vadr+=bl;
-                if((rcmd&(15<<28))==BT848_RISC_WRITE123)
-                {
-                       *((*rp)++)=cpu_to_le32(kvirt_to_bus(cbadr));
-                       cbadr+=blcb;
-                       *((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr));
-                       cradr+=blcr;
-                }
-                
-                rcmd&=~BT848_RISC_SOL; /* only the first has SOL */
-               }
-       }
-       
-       *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
-       *(ro++)=cpu_to_le32(btv->bus_vbi_even);
-       *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
-       *(re++)=cpu_to_le32(btv->bus_vbi_odd);
-       
-       if (bttv_debug > 1)
-               printk("bttv%d: prisc2: ro=%08lx re=%08lx\n",
-                      btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
-       return 0;
+       btv->input = input;
+       video_mux(btv,input);
+       audio_mux(btv,(input == bttv_tvcards[btv->type].tuner ?
+                      AUDIO_TUNER : AUDIO_EXTERN));
+       set_tvnorm(btv,btv->tvnorm);
 }
-static int  make_vrisctab(struct bttv *btv, unsigned int *ro,
-                          unsigned int *re, 
-                          unsigned int *vbuf, unsigned short width,
-                          unsigned short height, unsigned short palette)
+
+static void init_bt848(struct bttv *btv)
 {
-        unsigned long line;
-       unsigned long bpl;  /* bytes per line */
-       unsigned long bl;
-       unsigned long todo;
-       unsigned int **rp;
-       int inter;
-       unsigned long vadr=(unsigned long) vbuf;
-
-        if (palette==VIDEO_PALETTE_RAW) 
-                return make_rawrisctab(btv, ro, re, vbuf);
-        if (palette>=VIDEO_PALETTE_PLANAR)
-                return make_prisctab(btv, ro, re, vbuf, width, height, palette);
-
-       if (bttv_debug > 1)
-               printk("bttv%d: vrisc1: ro=%08lx re=%08lx\n",
-                      btv->nr,virt_to_bus(ro), virt_to_bus(re));
-       
-       inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
-       bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2;
+       int val;
        
-       *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); 
-        *(ro++)=cpu_to_le32(0);
-       *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
-        *(re++)=cpu_to_le32(0);
-  
-       for (line=0; line < (height<<(1^inter)); line++)
-       {
-               if (inter) 
-                       rp= (line&1) ? &re : &ro;
-               else 
-                       rp= (line>=height) ? &ro : &re; 
-
-               bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
-               if (bpl<=bl)
-                {
-                       *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
-                               BT848_RISC_EOL|bpl); 
-                       *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
-                       vadr+=bpl;
-               }
-               else
-               {
-                       todo=bpl;
-                       *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl);
-                       *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
-                       vadr+=bl;
-                       todo-=bl;
-                       while (todo>PAGE_SIZE)
-                       {
-                               *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE);
-                               *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
-                               vadr+=PAGE_SIZE;
-                               todo-=PAGE_SIZE;
-                       }
-                       *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo);
-                       *((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
-                       vadr+=todo;
-               }
+       btwrite(0, BT848_SRESET);
+       btwrite(0x00, BT848_CAP_CTL);
+       btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+       btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM);
+
+        /* set planar and packed mode trigger points and         */
+        /* set rising edge of inverted GPINTR pin as irq trigger */
+        btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
+                BT848_GPIO_DMA_CTL_PLTP1_16|
+                BT848_GPIO_DMA_CTL_PLTP23_16|
+                BT848_GPIO_DMA_CTL_GPINTC|
+                BT848_GPIO_DMA_CTL_GPINTI, 
+                BT848_GPIO_DMA_CTL);
+
+       val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+        btwrite(val, BT848_E_SCLOOP);
+        btwrite(val, BT848_O_SCLOOP);
+
+        btwrite(0x20, BT848_E_VSCALE_HI);
+        btwrite(0x20, BT848_O_VSCALE_HI);
+        btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+               BT848_ADC);
+
+       if (btv->opt_lumafilter) {
+               btwrite(0, BT848_E_CONTROL);
+               btwrite(0, BT848_O_CONTROL);
+       } else {
+               btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+               btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
        }
-       
-       *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
-       *(ro++)=cpu_to_le32(btv->bus_vbi_even);
-       *(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
-       *(re++)=cpu_to_le32(btv->bus_vbi_odd);
-
-       if (bttv_debug > 1)
-               printk("bttv%d: vrisc2: ro=%08lx re=%08lx\n",
-                      btv->nr,virt_to_bus(ro), virt_to_bus(re));
-       
-       return 0;
 }
 
-static unsigned char lmaskt[8] = 
-{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
-static unsigned char rmaskt[8] = 
-{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
-
-static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h)
+extern void bttv_reinit_bt848(struct bttv *btv)
 {
-        unsigned char lmask, rmask, *p;
-        int W, l, r;
-       int i;
+       unsigned long flags;
 
-       if (bttv_debug > 1)
-               printk("bttv clip: %dx%d+%d+%d\n",w,h,x,y);
-
-       /* bitmap is fixed width, 128 bytes (1024 pixels represented) */
-        if (x<0)
-        {
-                w+=x;
-                x=0;
-        }
-        if (y<0)
-        {
-                h+=y;
-                y=0;
-        }
-       if (w < 0 || h < 0)     /* catch bad clips */
-               return;
-       /* out of range data should just fall through */
-        if (y+h>=625)
-                h=625-y;
-        if (x+w>=1024)
-                w=1024-x;
-
-        l=x>>3;
-        r=(x+w-1)>>3;
-        W=r-l-1;
-        lmask=lmaskt[x&7];
-        rmask=rmaskt[(x+w-1)&7];
-        p=clipmap+128*y+l;
-        
-        if (W>0) 
-        {
-                for (i=0; i<h; i++, p+=128) 
-                {
-                        *p|=lmask;
-                        memset(p+1, 0xff, W);
-                        p[W+1]|=rmask;
-                }
-        } else if (!W) {
-                for (i=0; i<h; i++, p+=128) 
-                {
-                        p[0]|=lmask;
-                        p[1]|=rmask;
-                }
-        } else {
-                for (i=0; i<h; i++, p+=128) 
-                        p[0]|=lmask&rmask;
-        }
-               
+       if (bttv_verbose)
+               printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->nr);
+       spin_lock_irqsave(&btv->s_lock,flags);
+       btv->errors=0;
+       bttv_set_dma(btv,0,0);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
 
+       init_bt848(btv);
+        btv->pll.pll_current = -1;
+       set_input(btv,btv->input);
 }
 
-static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr)
+#ifdef HAVE_V4L2
+static int get_control(struct bttv *btv, struct v4l2_control *c)
 {
-       int i, line, x, y, bpl, width, height, inter, maxw;
-       unsigned int bpp, dx, sx, **rp, *ro, *re, flags, len;
-       unsigned long adr;
-       unsigned char *clipmap, *clipline, cbit, lastbit, outofmem;
-
-       /* take care: bpp != btv->win.bpp is allowed here */
-       bpp = fmtbppx2[btv->win.color_fmt&0xf]/2;
-       bpl=btv->win.bpl;
-       adr=btv->win.vidadr + btv->win.x * btv->win.bpp + btv->win.y * bpl;
-       inter=(btv->win.interlace&1)^1;
-       width=btv->win.width;
-       height=btv->win.height;
-       if (bttv_debug > 1)
-               printk("bttv%d: clip1: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
-                      btv->nr,btv->picture.palette,width,height,bpl,bpp);
-       if(width > 1023)
-               width = 1023;           /* sanity check */
-       if(height > 625)
-               height = 625;           /* sanity check */
-       ro=btv->risc_scr_odd;
-       re=btv->risc_scr_even;
-
-       if (bttv_debug)
-               printk("bttv%d: clip: ro=%08lx re=%08lx\n",
-                      btv->nr,virt_to_bus(ro), virt_to_bus(re));
-
-       if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
-               /* can't clip, don't generate any risc code */
-               *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
-               *(ro++)=cpu_to_le32(btv->bus_vbi_even);
-               *(re++)=cpu_to_le32(BT848_RISC_JUMP);
-               *(re++)=cpu_to_le32(btv->bus_vbi_odd);
-       }
-       if (ncr < 0) {  /* bitmap was pased */
-               memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE);
-       } else {        /* convert rectangular clips to a bitmap */
-               memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */
-               for (i=0; i<ncr; i++)
-                       clip_draw_rectangle(clipmap, cr[i].x, cr[i].y,
-                               cr[i].width, cr[i].height);
-       }
-       /* clip against viewing window AND screen 
-          so we do not have to rely on the user program
-        */
-       maxw = (bpl - btv->win.x * btv->win.bpp) / bpp;
-       clip_draw_rectangle(clipmap, (width > maxw) ? maxw : width,
-                           0, 1024, 768);
-       clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ?
-                           (btv->win.sheight-btv->win.y) : height,1024,768);
-       if (btv->win.x<0)
-               clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768);
-       if (btv->win.y<0)
-               clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y));
-       
-       *(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
-        *(ro++)=cpu_to_le32(0);
-       *(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
-        *(re++)=cpu_to_le32(0);
+       struct video_audio va;
+       int i;
        
-       /* translate bitmap to risc code */
-        for (line=outofmem=0; line < (height<<inter) && !outofmem; line++)
-        {
-               y = line>>inter;
-               rp= (line&1) ? &re : &ro;
-               clipline = clipmap + (y<<7); /* running pointers ... */
-               lastbit = *clipline & 1;
-               for(x=dx=0,sx=0; x<=width && !outofmem;) {
-                       if (0 == (x&7)) {
-                               /* check bytes not bits if we can ... */
-                               if (lastbit) {
-                                       while (0xff==*clipline && x<width-8) {
-                                               x  += 8;
-                                               dx += 8;
-                                               clipline++;
-                                       }
-                               } else {
-                                       while (0x00==*clipline && x<width-8) {
-                                               x  += 8;
-                                               dx += 8;
-                                               clipline++;
-                                       }
-                               }
-                       }
-                       cbit = *clipline & (1<<(x&7));
-                       if (x < width && !lastbit == !cbit) {
-                               dx++;
-                       } else {
-                               /* generate the dma controller code */
-                               len = dx * bpp;
-                               flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0);
-                               flags |= ((!sx) ? BT848_RISC_SOL : 0);
-                               flags |= ((sx + dx == width) ? BT848_RISC_EOL : 0);
-                               if (!lastbit) {
-                                       *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|flags|len);
-                                       *((*rp)++)=cpu_to_le32(adr + bpp * sx);
-                               } else {
-                                       *((*rp)++)=cpu_to_le32(BT848_RISC_SKIP|flags|len);
-                               }
-                               lastbit=cbit;
-                               sx += dx;
-                               dx = 1;
-                               if (ro - btv->risc_scr_odd>(RISCMEM_LEN>>3) - 16)
-                                       outofmem++;
-                               if (re - btv->risc_scr_even>(RISCMEM_LEN>>3) - 16)
-                                       outofmem++;
-                       }
-                       x++;
-                       if (0 == (x&7))
-                               clipline++;
-               }
-               if ((!inter)||(line&1))
-                        adr+=bpl;
+       for (i = 0; i < BTTV_CTLS; i++)
+               if (bttv_ctls[i].id == c->id)
+                       break;
+       if (i == BTTV_CTLS)
+               return -EINVAL;
+       if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+               memset(&va,0,sizeof(va));
+               bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+               if (bttv_tvcards[btv->type].audio_hook)
+                       bttv_tvcards[btv->type].audio_hook(btv,&va,0);
        }
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               c->value = btv->bright;
+               break;
+       case V4L2_CID_HUE:
+               c->value = btv->hue;
+               break;
+       case V4L2_CID_CONTRAST:
+               c->value = btv->contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               c->value = btv->saturation;
+               break;
 
-       vfree(clipmap);
-       /* outofmem flag relies on the following code to discard extra data */
-       *(ro++)=cpu_to_le32(BT848_RISC_JUMP);
-       *(ro++)=cpu_to_le32(btv->bus_vbi_even);
-       *(re++)=cpu_to_le32(BT848_RISC_JUMP);
-       *(re++)=cpu_to_le32(btv->bus_vbi_odd);
+       case V4L2_CID_AUDIO_MUTE:
+               c->value =  (VIDEO_AUDIO_MUTE == va.flags) ? 1 : 0;
+               break;
+       case V4L2_CID_AUDIO_VOLUME:
+               c->value = va.volume;
+               break;
+       case V4L2_CID_AUDIO_BALANCE:
+               c->value = va.balance;
+               break;
+       case V4L2_CID_AUDIO_BASS:
+               c->value = va.bass;
+               break;
+       case V4L2_CID_AUDIO_TREBLE:
+               c->value = va.treble;
+               break;
 
-       if (bttv_debug > 1)
-               printk("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
-                      btv->nr,btv->picture.palette,width,height,bpl,bpp);
+       case V4L2_CID_PRIVATE_CHROMA_AGC:
+               c->value = btv->opt_chroma_agc;
+               break;
+       case V4L2_CID_PRIVATE_COMBFILTER:
+               c->value = btv->opt_combfilter;
+               break;
+       case V4L2_CID_PRIVATE_LUMAFILTER:
+               c->value = btv->opt_lumafilter;
+               break;
+       case V4L2_CID_PRIVATE_AUTOMUTE:
+               c->value = btv->opt_automute;
+               break;
+       case V4L2_CID_PRIVATE_AGC_CRUSH:
+               c->value = btv->opt_adc_crush;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
 }
 
-/*
- *     Set the registers for the size we have specified. Don't bother
- *     trying to understand this without the BT848 manual in front of
- *     you [AC]. 
- *
- *     PS: The manual is free for download in .pdf format from 
- *     www.brooktree.com - nicely done those folks.
- */
-static inline void bt848_set_eogeo(struct bttv *btv, struct tvnorm *tvn,
-                                  int odd, int width, int height)
+static int set_control(struct bttv *btv, struct v4l2_control *c)
 {
-        u16 vscale, hscale;
-       u32 xsf, sr;
-       u16 hdelay;
-       u8 crop, vtc;
-       int inter = (height>tvn->sheight/2) ? 0 : 1;
-        int off = odd ? 0x80 : 0x00;
-
-       xsf = (width*tvn->scaledtwidth)/tvn->swidth;
-       hscale = ((tvn->totalwidth*4096UL)/xsf-4096);
-       hdelay =  tvn->hdelayx1;
-       hdelay =  (hdelay*width)/tvn->swidth;
-       hdelay &= 0x3fe;
-       sr=((tvn->sheight>>inter)*512)/height-512;
-       vscale=(0x10000UL-sr)&0x1fff;
-       crop=((width>>8)&0x03)|((hdelay>>6)&0x0c)|
-               ((tvn->sheight>>4)&0x30)|((tvn->vdelay>>2)&0xc0);
-       vscale |= inter ? (BT848_VSCALE_INT<<8) : 0;
-
-       if (combfilter) {
-               /* Some people say interpolation looks bad ... */
-               vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
-               if (width < 769)
-                       btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
-               else
-                       btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
-       } else {
-               vtc = 0;
-               btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
-       }
-
-       btwrite(vtc, BT848_E_VTC+off);
-       btwrite(hscale>>8, BT848_E_HSCALE_HI+off);
-       btwrite(hscale&0xff, BT848_E_HSCALE_LO+off);
-       btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
-       btwrite(vscale&0xff, BT848_E_VSCALE_LO+off);
-       btwrite(width&0xff, BT848_E_HACTIVE_LO+off);
-       btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off);
-       btwrite(tvn->sheight&0xff, BT848_E_VACTIVE_LO+off);
-       btwrite(tvn->vdelay&0xff, BT848_E_VDELAY_LO+off);
-       btwrite(crop, BT848_E_CROP+off);
+       struct video_audio va;
+       int i,val;
+
+       for (i = 0; i < BTTV_CTLS; i++)
+               if (bttv_ctls[i].id == c->id)
+                       break;
+       if (i == BTTV_CTLS)
+               return -EINVAL;
+       if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+               memset(&va,0,sizeof(va));
+               bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+               if (bttv_tvcards[btv->type].audio_hook)
+                       bttv_tvcards[btv->type].audio_hook(btv,&va,0);
+       }
+       switch (c->id) {
+       case V4L2_CID_BRIGHTNESS:
+               bt848_bright(btv,c->value);
+               break;
+       case V4L2_CID_HUE:
+               bt848_hue(btv,c->value);
+               break;
+       case V4L2_CID_CONTRAST:
+               bt848_contrast(btv,c->value);
+               break;
+       case V4L2_CID_SATURATION:
+               bt848_sat(btv,c->value);
+               break;
+       case V4L2_CID_AUDIO_MUTE:
+               if (c->value) {
+                       va.flags |= VIDEO_AUDIO_MUTE;
+                       audio_mux(btv, AUDIO_MUTE);
+               } else {
+                       va.flags &= ~VIDEO_AUDIO_MUTE;
+                       audio_mux(btv, AUDIO_UNMUTE);
+               }
+               break;
+
+       case V4L2_CID_AUDIO_VOLUME:
+               va.volume = c->value;
+               break;
+       case V4L2_CID_AUDIO_BALANCE:
+               va.balance = c->value;
+               break;
+       case V4L2_CID_AUDIO_BASS:
+               va.bass = c->value;
+               break;
+       case V4L2_CID_AUDIO_TREBLE:
+               va.treble = c->value;
+               break;
+
+       case V4L2_CID_PRIVATE_CHROMA_AGC:
+               btv->opt_chroma_agc = c->value;
+               val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0;
+               btwrite(val, BT848_E_SCLOOP);
+               btwrite(val, BT848_O_SCLOOP);
+               break;
+       case V4L2_CID_PRIVATE_COMBFILTER:
+               btv->opt_combfilter = c->value;
+               break;
+       case V4L2_CID_PRIVATE_LUMAFILTER:
+               btv->opt_lumafilter = c->value;
+               if (btv->opt_lumafilter) {
+                       btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL);
+                       btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               } else {
+                       btor(BT848_CONTROL_LDEC, BT848_E_CONTROL);
+                       btor(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               }
+               break;
+       case V4L2_CID_PRIVATE_AUTOMUTE:
+               btv->opt_automute = c->value;
+               break;
+       case V4L2_CID_PRIVATE_AGC_CRUSH:
+               btv->opt_adc_crush = c->value;
+               btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0),
+                       BT848_ADC);
+               break;
+       default:
+               return -EINVAL;
+       }
+       if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+               bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
+               if (bttv_tvcards[btv->type].audio_hook)
+                       bttv_tvcards[btv->type].audio_hook(btv,&va,1);
+       }
+       return 0;
 }
+#endif /* HAVE_V4L2 */
 
+/* ----------------------------------------------------------------------- */
 
-static void bt848_set_geo(struct bttv *btv,
-                         int no_irq_context)
+void bttv_gpio_tracking(struct bttv *btv, char *comment)
 {
-       u16 ewidth, eheight, owidth, oheight;
-       u16 format, bswap;
-       struct tvnorm *tvn;
+       unsigned int outbits, data;
+       outbits = btread(BT848_GPIO_OUT_EN);
+       data    = btread(BT848_GPIO_DATA);
+       printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
+              btv->nr,outbits,data & outbits, data & ~outbits, comment);
+}
 
-       tvn=&tvnorms[btv->win.norm];
-       
-       btwrite(tvn->adelay, BT848_ADELAY);
-       btwrite(tvn->bdelay, BT848_BDELAY);
-       btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM);
-       btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE);
-       btwrite(1, BT848_VBI_PACK_DEL);
+void bttv_field_count(struct bttv *btv)
+{
+       int need_count = 0;
 
-        btv->pll.pll_ofreq = tvn->Fsc;
-       if (no_irq_context)
-               set_pll(btv);
-
-       btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0;
-
-       if (0 == btv->risc_cap_odd &&
-           0 == btv->risc_cap_even) {
-               /* overlay only */
-               owidth  = btv->win.width;
-               oheight = btv->win.height;
-               ewidth  = btv->win.width;
-               eheight = btv->win.height;
-               format  = btv->win.color_fmt;
-               bswap   = btv->fb_color_ctl;
-       } else if (-1 != btv->gq_grab      &&
-                  0  == btv->risc_cap_odd &&
-                  !btv->win.interlace     &&
-                  btv->scr_on) {
-               /* odd field -> overlay, even field -> capture */
-               owidth  = btv->win.width;
-               oheight = btv->win.height;
-               ewidth  = btv->gbuf[btv->gq_grab].width;
-               eheight = btv->gbuf[btv->gq_grab].height;
-               format  = (btv->win.color_fmt & 0xf0) |
-                       (btv->gbuf[btv->gq_grab].fmt & 0x0f);
-               bswap   = btv->fb_color_ctl & 0x0a;
+       if (locked_btres(btv,RESOURCE_STREAMING))
+               need_count++;
+       if (btv->vbi.users)
+               need_count++;
+
+       if (need_count) {
+               /* start field counter */
+               btor(BT848_INT_VSYNC,BT848_INT_MASK);
        } else {
-               /* capture only */
-               owidth  = btv->gbuf[btv->gq_grab].width;
-               oheight = btv->gbuf[btv->gq_grab].height;
-               ewidth  = btv->gbuf[btv->gq_grab].width;
-               eheight = btv->gbuf[btv->gq_grab].height;
-               format  = btv->gbuf[btv->gq_grab].fmt;
-               bswap   = 0;
+               /* stop field counter */
+               btand(~BT848_INT_VSYNC,BT848_INT_MASK);
+               btv->field_count = 0;
        }
+}
 
-       /* program odd + even fields */
-       bt848_set_eogeo(btv, tvn, 1, owidth, oheight);
-       bt848_set_eogeo(btv, tvn, 0, ewidth, eheight);
+static const struct bttv_format*
+format_by_palette(int palette)
+{
+       int i;
 
-       btwrite(format, BT848_COLOR_FMT);
-       btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
+       for (i = 0; i < BTTV_FORMATS; i++) {
+               if (-1 == bttv_formats[i].palette)
+                       continue;
+               if (bttv_formats[i].palette == palette)
+                       return bttv_formats+i;
+       }
+       return NULL;
 }
 
+#ifdef HAVE_V4L2
+static const struct bttv_format*
+format_by_fourcc(int fourcc)
+{
+       int i;
 
-static int bpp2fmt[4] = {
-        BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16,
-        BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 
-};
+       for (i = 0; i < BTTV_FORMATS; i++) {
+               if (-1 == bttv_formats[i].fourcc)
+                       continue;
+               if (bttv_formats[i].fourcc == fourcc)
+                       return bttv_formats+i;
+       }
+       return NULL;
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* misc helpers                                                            */
 
-static void bt848_set_winsize(struct bttv *btv)
+static int
+bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
+                   struct bttv_buffer *new)
 {
-        unsigned short format;
+       struct bttv_buffer *old;
+       unsigned long flags;
+       int retval = 0;
+
+       if (new)
+               new->vb.state = STATE_DONE;
+       spin_lock_irqsave(&btv->s_lock,flags);
+       old = btv->screen;
+       btv->screen = new;
+       bttv_set_dma(btv, 0x03, 1);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+       if (NULL == new)
+               free_btres(btv,fh,RESOURCE_OVERLAY);
+       if (NULL != old)
+               bttv_dma_free(btv, old);
+       return retval;
+}
 
-       if (btv->picture.palette > 0 && btv->picture.palette <= VIDEO_PALETTE_YUV422) {
-               /* format set by VIDIOCSPICT */
-               format = palette2fmt[btv->picture.palette];
-       } else {
-               /* use default for the given color depth */
-               format = (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 :
-                       bpp2fmt[(btv->win.bpp-1)&3];
-       }
-       btv->win.color_fmt = format;
-       if (bigendian &&
-           format == BT848_COLOR_FMT_RGB32) {
-               btv->fb_color_ctl =
-                       BT848_COLOR_CTL_WSWAP_ODD       |
-                       BT848_COLOR_CTL_WSWAP_EVEN      |
-                       BT848_COLOR_CTL_BSWAP_ODD       |
-                       BT848_COLOR_CTL_BSWAP_EVEN;
-        } else if (bigendian &&
-                  (format == BT848_COLOR_FMT_RGB16 ||
-                    format == BT848_COLOR_FMT_RGB15)) {
-               btv->fb_color_ctl =
-                       BT848_COLOR_CTL_BSWAP_ODD       |
-                       BT848_COLOR_CTL_BSWAP_EVEN;
-        } else {
-               btv->fb_color_ctl = 0;
-       }
-
-       /*      RGB8 seems to be a 9x5x5 GRB color cube starting at
-        *      color 16. Why the h... can't they even mention this in the
-        *      data sheet?  [AC - because it's a standard format so I guess
-        *      it never occurred to them]
-        *      Enable dithering in this mode.
-        */
-
-       if (format==BT848_COLOR_FMT_RGB8)
-                btand(~BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); 
-       else
-               btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL);
-
-        bt848_set_geo(btv,1);
+static void
+bttv_stop_streaming(struct bttv *btv, struct bttv_fh *fh)
+{
+       unsigned long flags;
+       int i;
+
+       /* remove queued buffers from list */
+       spin_lock_irqsave(&btv->s_lock,flags);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == fh->bufs[i])
+                       continue;
+               if (fh->bufs[i]->vb.state == STATE_QUEUED) {
+                       list_del(&fh->bufs[i]->vb.queue);
+                       fh->bufs[i]->vb.state = STATE_ERROR;
+               }
+       }
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+
+       /* free all buffers + clear queue */
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == fh->bufs[i])
+                       continue;
+               bttv_dma_free(fh->btv,fh->bufs[i]);
+       }
+       INIT_LIST_HEAD(&fh->stream);
+       free_btres(btv,fh,RESOURCE_STREAMING);
+       bttv_field_count(btv);
 }
 
-/*
- *     Grab into virtual memory.
- */
+/* ----------------------------------------------------------------------- */
+/* video4linux (1) interface                                               */
 
-static int vgrab(struct bttv *btv, struct video_mmap *mp)
+static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
+                              const struct bttv_format *fmt,
+                              int width, int height, int field)
 {
-       unsigned int *ro, *re;
-       unsigned int *vbuf;
-       unsigned long flags;
+       int redo_dma_risc = 0;
+       int rc;
        
-       if(btv->fbuffer==NULL)
-       {
-               if(fbuffer_alloc(btv))
-                       return -ENOBUFS;
+       /* check settings */
+       if (NULL == fmt)
+               return -EINVAL;
+       if (fmt->btformat == BT848_COLOR_FMT_RAW) {
+               width  = RAW_BPL;
+               height = RAW_LINES*2;
+               if (width*height > buf->vb.bsize)
+                       return -EINVAL;
+               buf->vb.size = buf->vb.bsize;
+       } else {
+               if (width  < 48 ||
+                   height < 32 ||
+                   width  > bttv_tvnorms[btv->tvnorm].swidth ||
+                   height > bttv_tvnorms[btv->tvnorm].sheight)
+                       return -EINVAL;
+               buf->vb.size = (width * height * fmt->depth) >> 3;
+               if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+                       return -EINVAL;
        }
 
-       if(mp->frame >= gbuffers || mp->frame < 0)
-               return -EINVAL;
-       if(btv->gbuf[mp->frame].stat != GBUFFER_UNUSED)
-               return -EBUSY;
-               
-       if(mp->height < 32 || mp->width < 48)
-               return -EINVAL;
-       if (mp->format >= PALETTEFMT_MAX)
-               return -EINVAL;
+       /* alloc + fill struct bttv_buffer (if changed) */
+       if (buf->vb.width != width || buf->vb.height != height ||
+           buf->tvnorm != btv->tvnorm || buf->fmt != fmt) {
+               buf->vb.width  = width;
+               buf->vb.height = height;
+               buf->tvnorm    = btv->tvnorm;
+               buf->fmt       = fmt;
+               redo_dma_risc = 1;
+       }
 
-       if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2
-           > gbufsize)
-               return -EINVAL;
-       if(-1 == palette2fmt[mp->format])
-               return -EINVAL;
+       if (STATE_NEEDS_INIT == buf->vb.state) {
+               field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN,
+                                         btv->tvnorm, height);
+               if (field != buf->vb.field)
+                       redo_dma_risc = 1;
+               if (redo_dma_risc)
+                       bttv_dma_free(btv,buf);
+       }
 
-       /*
-        *      Ok load up the BT848
-        */
-        
-       vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame);
-       ro=btv->gbuf[mp->frame].risc;
-       re=ro+2048;
-        make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format);
-
-       if (bttv_debug)
-               printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n",
-                      btv->nr,mp->frame,mp->format,mp->width,mp->height);
-               spin_lock_irqsave(&btv->s_lock, flags); 
-        btv->gbuf[mp->frame].stat    = GBUFFER_GRABBING;
-       btv->gbuf[mp->frame].fmt     = palette2fmt[mp->format];
-       btv->gbuf[mp->frame].width   = mp->width;
-       btv->gbuf[mp->frame].height  = mp->height;
-       btv->gbuf[mp->frame].ro      = virt_to_bus(ro);
-       btv->gbuf[mp->frame].re      = virt_to_bus(re);
-
-#if 1
-       if (mp->height <= tvnorms[btv->win.norm].sheight/2 &&
-           mp->format != VIDEO_PALETTE_RAW)
-               btv->gbuf[mp->frame].ro = 0;
-#endif
+       /* alloc risc memory */
+       if (STATE_NEEDS_INIT == buf->vb.state) {
+               redo_dma_risc = 1;
+               if (0 != (rc = videobuf_iolock(btv->dev,&buf->vb)))
+                       goto fail;
+       }
 
-       if (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) {
-               btv->gq_start = 1;
-               btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
-        }
-       btv->gqueue[btv->gq_in++] = mp->frame;
-       btv->gq_in = btv->gq_in % MAX_GBUFFERS;
+       if (redo_dma_risc)
+               if (0 != (rc = bttv_buffer_risc(btv,buf)))
+                       goto fail;
 
-       btor(3, BT848_CAP_CTL);
-       btor(3, BT848_GPIO_DMA_CTL);
-       spin_unlock_irqrestore(&btv->s_lock, flags);
+       buf->vb.state = STATE_PREPARED;
        return 0;
+
+ fail:
+       bttv_dma_free(btv,buf);
+       return rc;
 }
 
-static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
+static void
+bttv_queue_buffer(struct bttv *btv, struct bttv_buffer *buf)
 {
-       return -EINVAL;
+       unsigned long flags;
+       
+       buf->vb.state = STATE_QUEUED;
+       spin_lock_irqsave(&btv->s_lock,flags);
+       list_add_tail(&buf->vb.queue,&btv->capture);
+       bttv_set_dma(btv, 0x03, 1);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
 }
 
-static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+static const char *v4l1_ioctls[] = {
+       "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT",
+       "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ",
+       "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT",
+       "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO",
+       "SMICROCODE", "GVBIFMT", "SVBIFMT" };
+#define V4L1_IOCTLS (sizeof(v4l1_ioctls)/sizeof(char*))
+
+static const char *v4l2_ioctls[] = {
+       "QUERYCAP", "1", "ENUM_PIXFMT", "ENUM_FBUFFMT", "G_FMT", "S_FMT",
+       "G_COMP", "S_COMP", "REQBUFS", "QUERYBUF", "G_FBUF", "S_FBUF",
+       "G_WIN", "S_WIN", "PREVIEW", "QBUF", "16", "DQBUF", "STREAMON",
+       "STREAMOFF", "G_PERF", "G_PARM", "S_PARM", "G_STD", "S_STD",
+       "ENUMSTD", "ENUMINPUT", "G_CTRL", "S_CTRL", "G_TUNER", "S_TUNER",
+       "G_FREQ", "S_FREQ", "G_AUDIO", "S_AUDIO", "35", "QUERYCTRL",
+       "QUERYMENU", "G_INPUT", "S_INPUT", "ENUMCVT", "41", "42", "43",
+       "44", "45",  "G_OUTPUT", "S_OUTPUT", "ENUMOUTPUT", "G_AUDOUT",
+       "S_AUDOUT", "ENUMFX", "G_EFFECT", "S_EFFECT", "G_MODULATOR",
+       "S_MODULATOR"
+};
+#define V4L2_IOCTLS (sizeof(v4l2_ioctls)/sizeof(char*))
+
+int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
 {
-       struct bttv *btv= (struct bttv *)v;
-       int q,todo;
-       DECLARE_WAITQUEUE(wait, current);
+       switch (cmd) {
+        case BTTV_VERSION:
+                return BTTV_VERSION_CODE;
 
-       /* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */
-       todo=count;
-       while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) 
+       /* ***  v4l1  *** ************************************************ */
+       case VIDIOCGFREQ:
+#ifdef HAVE_V4L2
+       case VIDIOC_G_FREQ:
+#endif
        {
-               if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
-                       return -EFAULT;
-               todo-=q;
-               buf+=q;
-
-               add_wait_queue(&btv->vbiq, &wait);
-               current->state = TASK_INTERRUPTIBLE;
-               if (todo && q==VBIBUF_SIZE-btv->vbip) 
-               {
-                       if(nonblock)
-                       {
-                               remove_wait_queue(&btv->vbiq, &wait);
-                                current->state = TASK_RUNNING;
-                               if(count==todo)
-                                       return -EWOULDBLOCK;
-                               return count-todo;
-                       }
-                       schedule();     
-                       if(signal_pending(current))
-                       {
-                               remove_wait_queue(&btv->vbiq, &wait);
-                                current->state = TASK_RUNNING;
-
-                               if(todo==count)
-                                       return -EINTR;
-                               else
-                                       return count-todo;
-                       }
-               }
-               remove_wait_queue(&btv->vbiq, &wait);
-                current->state = TASK_RUNNING;
+               unsigned long *freq = arg;
+               *freq = btv->freq;
+               return 0;
        }
-       if (todo) 
+       case VIDIOCSFREQ:
+#ifdef HAVE_V4L2
+       case VIDIOC_S_FREQ:
+#endif
        {
-               if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
-                       return -EFAULT;
-               btv->vbip+=todo;
+               unsigned long *freq = arg;
+               down(&btv->lock);
+               btv->freq=*freq;
+               bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq);
+               if (btv->has_matchbox && btv->radio_user)
+                       tea5757_set_freq(btv,*freq);
+               up(&btv->lock);
+               return 0;
        }
-       return count;
-}
 
-static inline void burst(int on)
-{
-       tvnorms[0].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0);
-       tvnorms[0].hdelayx1     = 186  - (on?BURSTOFFSET  :0);
-       tvnorms[2].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0);
-       tvnorms[2].hdelayx1     = 186  - (on?BURSTOFFSET  :0);
-}
+       case VIDIOCGTUNER:
+       {
+               struct video_tuner *v = arg;
+               
+               if (v->tuner) /* Only tuner 0 */
+                       return -EINVAL;
+               strcpy(v->name, "Television");
+               v->rangelow  = 0;
+               v->rangehigh = 0x7FFFFFFF;
+               v->flags     = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
+               v->mode      = btv->tvnorm;
+               v->signal    = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
+               bttv_call_i2c_clients(btv,cmd,v);
+               return 0;
+       }
+       case VIDIOCSTUNER:
+       {
+               struct video_tuner *v = arg;
 
-/*
- * called from irq handler on fatal errors.  Takes the grabber chip
- * offline, flag it needs a reinitialization (which can't be done
- * from irq context) and wake up all sleeping proccesses.  They would
- * block forever else.  We also need someone who actually does the
- * reinitialization from process context...
- */
-static void bt848_offline(struct bttv *btv)
-{
-       int i;
-       spin_lock(&btv->s_lock);
+               if (v->tuner) /* Only tuner 0 */
+                       return -EINVAL;
+               if (v->mode >= BTTV_TVNORMS)
+                       return -EINVAL;
 
-       /* cancel all outstanding grab requests */
-       btv->gq_in = 0;
-       btv->gq_out = 0;
-       btv->gq_grab = -1;
-       for (i = 0; i < gbuffers; i++)
-               if (btv->gbuf[i].stat == GBUFFER_GRABBING)
-                       btv->gbuf[i].stat = GBUFFER_ERROR;
-
-       /* disable screen overlay and DMA */
-       btv->risc_cap_odd  = 0;
-       btv->risc_cap_even = 0;
-       bt848_set_risc_jmps(btv,0);
-
-       /* flag the chip needs a restart */
-       btv->needs_restart = 1;
-       spin_unlock(&btv->s_lock);
+               down(&btv->lock);
+               set_tvnorm(btv,v->mode);
+               bttv_call_i2c_clients(btv,cmd,v);
+               up(&btv->lock);
+               return 0;
+       }
+       
+        case VIDIOCGCHAN:
+        {
+                struct video_channel *v = arg;
 
-       wake_up_interruptible(&btv->vbiq);
-       wake_up_interruptible(&btv->capq);
-}
+                if (v->channel >= bttv_tvcards[btv->type].video_inputs)
+                        return -EINVAL;
+                v->tuners=0;
+                v->flags = VIDEO_VC_AUDIO;
+                v->type = VIDEO_TYPE_CAMERA;
+                v->norm = btv->tvnorm;
+                if(v->channel == bttv_tvcards[btv->type].tuner)  {
+                        strcpy(v->name,"Television");
+                        v->flags|=VIDEO_VC_TUNER;
+                        v->type=VIDEO_TYPE_TV;
+                        v->tuners=1;
+                } else if (v->channel == bttv_tvcards[btv->type].svhs) {
+                        strcpy(v->name,"S-Video");
+                } else {
+                        sprintf(v->name,"Composite%d",v->channel);
+               }
+               return 0;
+        }
+        case VIDIOCSCHAN:
+        {
+                struct video_channel *v = arg;
 
-static void bt848_restart(struct bttv *btv)
-{
-       unsigned long irq_flags;
+               if (v->channel <  0 ||
+                   v->channel >= bttv_tvcards[btv->type].video_inputs)
+                       return -EINVAL;
+               if (v->norm >= BTTV_TVNORMS)
+                       return -EINVAL;
 
-       if (bttv_verbose)
-               printk("bttv%d: resetting chip\n",btv->nr);
-       btwrite(0xfffffUL, BT848_INT_STAT);
-       btand(~15, BT848_GPIO_DMA_CTL);
-       btwrite(0, BT848_SRESET);
-       btwrite(virt_to_bus(btv->risc_jmp+2),
-               BT848_RISC_STRT_ADD);
+               down(&btv->lock);
+               if (v->channel == btv->input &&
+                   v->norm    == btv->tvnorm) {
+                       /* nothing to do */
+                       up(&btv->lock);
+                       return 0;
+               }
 
-       /* enforce pll reprogramming */
-       btv->pll.pll_current = 0;
-       set_pll(btv);
+               btv->tvnorm = v->norm;
+               set_input(btv,v->channel);
+               up(&btv->lock);
+               return 0;
+       }
 
-       spin_lock_irqsave(&btv->s_lock, irq_flags);
-       btv->errors = 0;
-       btv->needs_restart = 0;
-       bt848_set_geo(btv,0);
-       bt848_set_risc_jmps(btv,-1);
-       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-}
+        case VIDIOCGAUDIO:
+       {
+               struct video_audio *v = arg;
 
-/*
- *     Open a bttv card. Right now the flags stuff is just playing
- */
+               memset(v,0,sizeof(*v));
+               strcpy(v->name,"Television");
+               v->flags |= VIDEO_AUDIO_MUTABLE;
+               v->mode  = VIDEO_SOUND_MONO;
 
-static int bttv_open(struct video_device *dev, int flags)
-{
-       struct bttv *btv = (struct bttv *)dev;
-        int i,ret;
+               down(&btv->lock);
+               bttv_call_i2c_clients(btv,cmd,v);
 
-       ret = -EBUSY;
-       if (bttv_debug)
-               printk("bttv%d: open called\n",btv->nr);
+               /* card specific hooks */
+               if (bttv_tvcards[btv->type].audio_hook)
+                       bttv_tvcards[btv->type].audio_hook(btv,v,0);
 
-       down(&btv->lock);
-       if (btv->user)
-               goto out_unlock;
-       
-       btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
-       ret = -ENOMEM;
-       if (!btv->fbuffer)
-               goto out_unlock;
-
-        btv->gq_in = 0;
-        btv->gq_out = 0;
-       btv->gq_grab = -1;
-        for (i = 0; i < gbuffers; i++)
-                btv->gbuf[i].stat = GBUFFER_UNUSED;
-
-       if (btv->needs_restart)
-               bt848_restart(btv);
-        burst(0);
-       set_pll(btv);
-        btv->user++;
-       up(&btv->lock);
-        return 0;
+               up(&btv->lock);
+               return 0;
+       }
+       case VIDIOCSAUDIO:
+       {
+               struct video_audio *v = arg;
 
- out_unlock:
-       up(&btv->lock);
-       return ret;
-}
+               if(v->audio <  0 ||
+                  v->audio >= bttv_tvcards[btv->type].audio_inputs)
+                       return -EINVAL;
 
-static void bttv_close(struct video_device *dev)
-{
-       struct bttv *btv=(struct bttv *)dev;
-       unsigned long irq_flags;
-       int need_wait;
+               down(&btv->lock);
+               audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE);
+               bttv_call_i2c_clients(btv,cmd,v);
 
-       down(&btv->lock);
-       btv->user--;
-       spin_lock_irqsave(&btv->s_lock, irq_flags);
-       need_wait = (-1 != btv->gq_grab);
-       btv->gq_start = 0;
-       btv->gq_in = 0;
-       btv->gq_out = 0;
-       btv->gq_grab = -1;
-       btv->scr_on = 0;
-       btv->risc_cap_odd = 0;
-       btv->risc_cap_even = 0;
-       bt848_set_risc_jmps(btv,-1);
-       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-
-       /*
-        *      A word of warning. At this point the chip
-        *      is still capturing because its FIFO hasn't emptied
-        *      and the DMA control operations are posted PCI 
-        *      operations.
-        */
-
-       btread(BT848_I2C);      /* This fixes the PCI posting delay */
-       
-       if (need_wait) {
-               /*
-                *      This is sucky but right now I can't find a good way to
-                *      be sure its safe to free the buffer. We wait 5-6 fields
-                *      which is more than sufficient to be sure.
-                */
-               current->state = TASK_UNINTERRUPTIBLE;
-               schedule_timeout(HZ/10);        /* Wait 1/10th of a second */
+               /* card specific hooks */
+               if (bttv_tvcards[btv->type].audio_hook)
+                       bttv_tvcards[btv->type].audio_hook(btv,v,1);
+               
+               up(&btv->lock);
+               return 0;
        }
-       
-       /*
-        *      We have allowed it to drain.
-        */
 
-       if(btv->fbuffer)
-               rvfree((void *) btv->fbuffer, gbuffers*gbufsize);
-       btv->fbuffer=0;
-       up(&btv->lock);
-}
+#ifdef HAVE_V4L2
+       /* ***  v4l2  *** ************************************************ */
+       case VIDIOC_ENUMSTD:
+       {
+               struct v4l2_enumstd *e = arg;
 
+               if (e->index < 0 || e->index >= BTTV_TVNORMS)
+                       return -EINVAL;
+               v4l2_video_std_construct(&e->std, bttv_tvnorms[e->index].v4l2_id, 0);
+               e->inputs  = 0x0f;
+               e->outputs = 0x00;
+               return 0;
+       }
+       case VIDIOC_G_STD:
+       {
+               struct v4l2_standard *s = arg;
+               v4l2_video_std_construct(s,bttv_tvnorms[btv->tvnorm].v4l2_id,0);
+               return 0;
+       }
+       case VIDIOC_S_STD:
+       {
+               struct v4l2_standard *s = arg;
+               int i, id = v4l2_video_std_confirm(s);
 
-/***********************************/
-/* ioctls and supporting functions */
-/***********************************/
+               for(i = 0; i < BTTV_TVNORMS; i++)
+                       if (id == bttv_tvnorms[i].v4l2_id)
+                               break;
+               if (i == BTTV_TVNORMS)
+                       return -EINVAL;
 
-extern inline void bt848_bright(struct bttv *btv, uint bright)
-{
-       btwrite(bright&0xff, BT848_BRIGHT);
-}
+               down(&btv->lock);
+               set_tvnorm(btv,i);
+               i2c_vidiocschan(btv);
+               up(&btv->lock);
+               return 0;
+       }
 
-extern inline void bt848_hue(struct bttv *btv, uint hue)
-{
-       btwrite(hue&0xff, BT848_HUE);
-}
+       case VIDIOC_ENUMINPUT:
+       {
+               struct v4l2_input *i = arg;
 
-extern inline void bt848_contrast(struct bttv *btv, uint cont)
-{
-       unsigned int conthi;
+               if (i->index >= bttv_tvcards[btv->type].video_inputs)
+                       return -EINVAL;
+               i->type = V4L2_INPUT_TYPE_CAMERA;
+               i->capability = 0;
+               i->assoc_audio = 0;
+               if (i->index == bttv_tvcards[btv->type].tuner) {
+                       sprintf(i->name, "Television");
+                       i->type = V4L2_INPUT_TYPE_TUNER;
+                       i->capability = V4L2_INPUT_CAP_AUDIO;
+               } else if (i->index==bttv_tvcards[btv->type].svhs) {
+                       sprintf(i->name, "S-Video");
+               } else {
+                        sprintf(i->name,"Composite%d",i->index);
+               }
+               return 0;
+       }
+       case VIDIOC_G_INPUT:
+       {
+               int *i = arg;
+               *i = btv->input;
+               return 0;
+       }
+       case VIDIOC_S_INPUT:
+       {
+               int *i = arg;
+               
+               if (*i < 0 || *i > bttv_tvcards[btv->type].video_inputs)
+                       return -EINVAL;
+               down(&btv->lock);
+               set_input(btv,*i);
+               i2c_vidiocschan(btv);
+               up(&btv->lock);
+               return 0;
+       }
+       
+       case VIDIOC_G_TUNER: {
+               struct v4l2_tuner *t = arg;
 
-       conthi=(cont>>6)&4;
-       btwrite(cont&0xff, BT848_CONTRAST_LO);
-       btaor(conthi, ~4, BT848_E_CONTROL);
-       btaor(conthi, ~4, BT848_O_CONTROL);
+               down(&btv->lock);
+               memset(t,0,sizeof(*t));
+               t->input = bttv_tvcards[btv->type].tuner;
+               strcpy(t->name, "Television");
+               v4l2_video_std_construct(&t->std, bttv_tvnorms[btv->tvnorm].v4l2_id, 0);
+               t->capability = V4L2_TUNER_CAP_NORM;
+               t->rangehigh = 0xffffffffUL;
+               t->rxsubchans = V4L2_TUNER_SUB_MONO;
+               if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)
+                       t->signal = 0xffff;
+               {
+                       /* Hmmm ... */
+                       struct video_audio va;
+                       memset(&va, 0, sizeof(struct video_audio));
+                       bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+                       if (bttv_tvcards[btv->type].audio_hook)
+                               bttv_tvcards[btv->type].audio_hook(btv,&va,0);
+                       if(va.mode & VIDEO_SOUND_STEREO)
+                               t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
+                       if(va.mode & VIDEO_SOUND_LANG1)
+                               t->rxsubchans |= V4L2_TUNER_SUB_LANG1;
+                       if(va.mode & VIDEO_SOUND_LANG2)
+                               t->rxsubchans |= V4L2_TUNER_SUB_LANG2;
+               }
+               /* FIXME: fill capability+audmode */
+               up(&btv->lock);
+               return 0;
+       }
+       case VIDIOC_S_TUNER: {
+               struct v4l2_tuner *t = arg;
+               
+               if(t->input!=bttv_tvcards[btv->type].tuner)
+                       return -EINVAL;
+               down(&btv->lock);
+               {
+                       struct video_audio va;
+                       memset(&va, 0, sizeof(struct video_audio));
+                       if (t->audmode == V4L2_TUNER_MODE_MONO)
+                               va.mode = VIDEO_SOUND_MONO;
+                       else if (t->audmode == V4L2_TUNER_MODE_STEREO)
+                               va.mode = VIDEO_SOUND_STEREO;
+                       else if (t->audmode == V4L2_TUNER_MODE_LANG1)
+                               va.mode = VIDEO_SOUND_LANG1;
+                       else if (t->audmode == V4L2_TUNER_MODE_LANG2)
+                               va.mode = VIDEO_SOUND_LANG2;
+                       bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
+                       if (bttv_tvcards[btv->type].audio_hook)
+                               bttv_tvcards[btv->type].audio_hook(btv,&va,1);
+               }
+               up(&btv->lock);
+               return 0;
+       }
+#endif /* HAVE_V4L2 */
+
+       default:
+               return -ENOIOCTLCMD;
+       
+       }
+       return 0;
 }
 
-extern inline void bt848_sat_u(struct bttv *btv, unsigned long data)
+static int setup_window(struct bttv_fh *fh, struct bttv *btv,
+                       int x, int y, int width, int height,
+                       struct video_clip *user_clips, int nclips)
 {
-       u32 datahi;
+       struct video_clip *clips = NULL;
+       int n,size,retval = 0;
+       
+       if (width  < 48 ||
+           height < 32 ||
+           width  > bttv_tvnorms[btv->tvnorm].swidth ||
+           height > bttv_tvnorms[btv->tvnorm].sheight ||
+           NULL == fh->ovfmt)
+               return -EINVAL;
+       if (nclips > 2048)
+               return -EINVAL;
 
-       datahi=(data>>7)&2;
-       btwrite(data&0xff, BT848_SAT_U_LO);
-       btaor(datahi, ~2, BT848_E_CONTROL);
-       btaor(datahi, ~2, BT848_O_CONTROL);
+       /* copy clips  --  luckily v4l1 + v4l2 are binary
+          compatible here ...*/
+       n = nclips;
+       size = sizeof(struct video_clip)*(n+4);
+       clips = kmalloc(size,GFP_KERNEL);
+       if (NULL == clips)
+               return -ENOMEM;
+       if (n > 0) {
+               if (copy_from_user(clips,user_clips,
+                                  sizeof(struct video_clip)*nclips)) {
+                       kfree(clips);
+                       return -EFAULT;
+               }
+       }
+       /* clip against screen */
+       if (NULL != btv->fbuf.base)
+               n = bttv_screen_clips(&btv->fbuf, x, y,
+                                     width, height,
+                                     clips, n);
+       bttv_sort_clips(clips,nclips);
+       
+       down(&fh->lock);
+       if (fh->ov.clips)
+               kfree(fh->ov.clips);
+       fh->ov.clips   = clips;
+       fh->ov.nclips  = nclips;
+       
+       fh->ov.x       = x;
+       fh->ov.y       = y;
+       fh->ov.width   = width;
+       fh->ov.height  = height;
+       btv->init.ov.width   = width;
+       btv->init.ov.height  = height;
+       
+       /* update overlay if needed */
+       retval = 0;
+       if (check_btres(fh, RESOURCE_OVERLAY)) {
+               struct bttv_buffer *new;
+               
+               new = videobuf_alloc(sizeof(*new),V4L2_BUF_TYPE_CAPTURE);
+               bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+               retval = bttv_switch_overlay(btv,fh,new);
+       }
+       up(&fh->lock);
+       return retval;
 }
 
-static inline void bt848_sat_v(struct bttv *btv, unsigned long data)
+static void release_buffer(struct file *file, struct videobuf_buffer *vb)
 {
-       u32 datahi;
+       struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+       struct bttv_fh *fh = file->private_data;
 
-       datahi=(data>>8)&1;
-       btwrite(data&0xff, BT848_SAT_V_LO);
-       btaor(datahi, ~1, BT848_E_CONTROL);
-       btaor(datahi, ~1, BT848_O_CONTROL);
+       bttv_dma_free(fh->btv,buf);
 }
 
-/*
- *     ioctl routine
- */
-
-static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int bttv_ioctl(struct inode *inode, struct file *file,
+                     unsigned int cmd, void *arg)
 {
-       struct bttv *btv=(struct bttv *)dev;
-       unsigned long irq_flags;
-       int i,ret = 0;
-
-       if (bttv_debug > 1)
-               printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd);
+       struct bttv_fh *fh  = file->private_data;
+       struct bttv    *btv = fh->btv;
+       int retval;
+
+       if (bttv_debug > 1) {
+               switch (_IOC_TYPE(cmd)) {
+               case 'v':
+                       printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n",
+                              btv->nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ?
+                              v4l1_ioctls[_IOC_NR(cmd)] : "???");
+                       break;
+               case 'V':
+                       printk("bttv%d: ioctl 0x%x (v4l2, VIDIOC_%s)\n",
+                              btv->nr, cmd,  (_IOC_NR(cmd) < V4L2_IOCTLS) ?
+                              v4l2_ioctls[_IOC_NR(cmd)] : "???");
+                       break;
+               default:
+                       printk("bttv%d: ioctl 0x%x (???)\n",
+                              btv->nr, cmd);
+               }
+       }
+       if (btv->errors)
+               bttv_reinit_bt848(btv);
 
        switch (cmd) {
+
+       /* ***  v4l1  *** ************************************************ */
        case VIDIOCGCAP:
        {
-               struct video_capability b;
-               strcpy(b.name,btv->video_dev.name);
-               b.type = VID_TYPE_CAPTURE|
-                       ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) |
+                struct video_capability *cap = arg;
+
+               memset(cap,0,sizeof(*cap));
+                strcpy(cap->name,btv->video_dev.name);
+                cap->type = VID_TYPE_CAPTURE|
+                       VID_TYPE_TUNER|
                        VID_TYPE_OVERLAY|
                        VID_TYPE_CLIPPING|
-                       VID_TYPE_FRAMERAM|
-                       VID_TYPE_SCALES;
-               b.channels = bttv_tvcards[btv->type].video_inputs;
-               b.audios = bttv_tvcards[btv->type].audio_inputs;
-               b.maxwidth = tvnorms[btv->win.norm].swidth;
-               b.maxheight = tvnorms[btv->win.norm].sheight;
-               b.minwidth = 48;
-               b.minheight = 32;
-               if(copy_to_user(arg,&b,sizeof(b)))
-                       return -EFAULT;
-               return 0;
+                        VID_TYPE_SCALES;
+                cap->channels  = bttv_tvcards[btv->type].video_inputs;
+                cap->audios    = bttv_tvcards[btv->type].audio_inputs;
+                cap->maxwidth  = bttv_tvnorms[btv->tvnorm].swidth;
+                cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
+                cap->minwidth  = 48;
+                cap->minheight = 32;
+                return 0;
        }
-       case VIDIOCGCHAN:
+
+       case VIDIOCGPICT:
        {
-               struct video_channel v;
-               if(copy_from_user(&v, arg,sizeof(v)))
-                       return -EFAULT;
-               v.flags=VIDEO_VC_AUDIO;
-               v.tuners=0;
-               v.type=VIDEO_TYPE_CAMERA;
-               v.norm = btv->win.norm;
-               if (v.channel>=bttv_tvcards[btv->type].video_inputs)
-                       return -EINVAL;
-               if(v.channel==bttv_tvcards[btv->type].tuner) 
-               {
-                       strcpy(v.name,"Television");
-                       v.flags|=VIDEO_VC_TUNER;
-                       v.type=VIDEO_TYPE_TV;
-                       v.tuners=1;
-               } 
-               else if(v.channel==bttv_tvcards[btv->type].svhs) 
-                       strcpy(v.name,"S-Video");
-               else
-                       sprintf(v.name,"Composite%d",v.channel);
-               
-               if(copy_to_user(arg,&v,sizeof(v)))
-                       return -EFAULT;
+               struct video_picture *pic = arg;
+
+               memset(pic,0,sizeof(*pic));
+               pic->brightness = btv->bright;
+               pic->contrast   = btv->contrast;
+               pic->hue        = btv->hue;
+               pic->colour     = btv->saturation;
+               if (fh->buf.fmt) {
+                       pic->depth   = fh->buf.fmt->depth;
+                       pic->palette = fh->buf.fmt->palette;
+               }
                return 0;
        }
-       /*
-        *      Each channel has 1 tuner
-        */
-       case VIDIOCSCHAN:
+       case VIDIOCSPICT:
        {
-               struct video_channel v;
-               if(copy_from_user(&v, arg,sizeof(v)))
-                       return -EFAULT;
+               struct video_picture *pic = arg;
+               const struct bttv_format *fmt;
                
-               if (v.channel>bttv_tvcards[btv->type].video_inputs)
+               fmt = format_by_palette(pic->palette);
+               if (NULL == fmt)
                        return -EINVAL;
-               if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms)))
-                       return -EOPNOTSUPP;
+               down(&fh->lock);
+               retval = -EINVAL;
+               if (fmt->depth != pic->depth && !sloppy)
+                       goto fh_unlock_and_return;
+               fh->ovfmt   = fmt;
+               fh->buf.fmt = fmt;
+               btv->init.ovfmt   = fmt;
+               btv->init.buf.fmt = fmt;
+               if (bigendian) {
+                       /* dirty hack time:  swap bytes for overlay if the
+                          display adaptor is big endian (insmod option) */
+                       if (fmt->palette == VIDEO_PALETTE_RGB555 ||
+                           fmt->palette == VIDEO_PALETTE_RGB565 ||
+                           fmt->palette == VIDEO_PALETTE_RGB32) {
+                               fh->ovfmt = fmt+1;
+                       }
+               }
+               bt848_bright(btv,pic->brightness);
+               bt848_contrast(btv,pic->contrast);
+               bt848_hue(btv,pic->hue);
+               bt848_sat(btv,pic->colour);
+               up(&fh->lock);
+                return 0;
+       }
 
-               bttv_call_i2c_clients(btv,cmd,&v);
-               down(&btv->lock);
-               bt848_muxsel(btv, v.channel);
-               btv->channel=v.channel;
-               if (btv->win.norm != v.norm) {
-                       btv->win.norm = v.norm;
-                       make_vbitab(btv);
-                       spin_lock_irqsave(&btv->s_lock, irq_flags);
-                       bt848_set_winsize(btv);
-                       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+       case VIDIOCGWIN:
+       {
+               struct video_window *win = arg;
+
+               memset(win,0,sizeof(*win));
+               win->x      = fh->ov.x;
+               win->y      = fh->ov.y;
+               win->width  = fh->ov.width;
+               win->height = fh->ov.height;
+               return 0;
+       }
+       case VIDIOCSWIN:
+       {
+               struct video_window *win = arg;
+
+               retval = setup_window(fh,btv,win->x,win->y,
+                                     win->width,win->height,
+                                     win->clips,
+                                     win->clipcount);
+               if (0 == retval) {
+                       /* on v4l1 this ioctl affects the read() size too */
+                       fh->buf.vb.width  = fh->ov.width;
+                       fh->buf.vb.height = fh->ov.height;
+                       btv->init.buf.vb.width  = fh->ov.width;
+                       btv->init.buf.vb.height = fh->ov.height;
                }
-               up(&btv->lock);
+               return retval;
+       }
+
+       case VIDIOCGFBUF:
+       {
+               struct video_buffer *fbuf = arg;
+               *fbuf = btv->fbuf;
                return 0;
        }
-       case VIDIOCGTUNER:
+       case VIDIOCSFBUF:
        {
-               struct video_tuner v;
-               if(copy_from_user(&v,arg,sizeof(v))!=0)
-                       return -EFAULT;
-#if 0 /* tuner.signal might be of intrest for non-tuner sources too ... */
-               if(v.tuner||btv->channel)       /* Only tuner 0 */
+               struct video_buffer *fbuf = arg;
+               const struct bttv_format *fmt;
+               unsigned long end;
+
+               if(!capable(CAP_SYS_ADMIN) &&
+                   !capable(CAP_SYS_RAWIO))
+                        return -EPERM;
+               end = (unsigned long)fbuf->base +
+                       fbuf->height * fbuf->bytesperline;
+               if (0 == find_videomem((unsigned long)fbuf->base,end))
                        return -EINVAL;
-#endif
-               strcpy(v.name, "Television");
-               v.rangelow=0;
-               v.rangehigh=0xFFFFFFFF;
-               v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
-               v.mode = btv->win.norm;
-               v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
-               bttv_call_i2c_clients(btv,cmd,&v);
-               if(copy_to_user(arg,&v,sizeof(v)))
-                       return -EFAULT;
+               down(&fh->lock);
+               retval = -EINVAL;
+               if (sloppy) {
+                       /* also set the default palette -- for backward
+                          compatibility with older versions */
+                       switch (fbuf->depth) {
+                       case 8:
+                               fmt = format_by_palette(VIDEO_PALETTE_HI240);
+                               break;
+                       case 16:
+                               fmt = format_by_palette(VIDEO_PALETTE_RGB565);
+                               break;
+                       case 24:
+                               fmt = format_by_palette(VIDEO_PALETTE_RGB24);
+                               break;
+                       case 32:
+                               fmt = format_by_palette(VIDEO_PALETTE_RGB32);
+                               break;
+                       case 15:
+                               fbuf->depth = 16;
+                               fmt = format_by_palette(VIDEO_PALETTE_RGB555);
+                               break;
+                       default:
+                               fmt = NULL;
+                               break;
+                       }
+                       if (NULL == fmt)
+                               goto fh_unlock_and_return;
+                       fh->ovfmt   = fmt;
+                       fh->buf.fmt = fmt;
+                       btv->init.ovfmt   = fmt;
+                       btv->init.buf.fmt = fmt;
+               } else {
+                       if (15 == fbuf->depth)
+                               fbuf->depth = 16;
+                       if (fbuf->depth !=  8 && fbuf->depth != 16 &&
+                           fbuf->depth != 24 && fbuf->depth != 32)
+                               goto fh_unlock_and_return;
+               }
+               btv->fbuf = *fbuf;
+               up(&fh->lock);
                return 0;
        }
-       /* We have but one tuner */
-       case VIDIOCSTUNER:
+
+       case VIDIOCCAPTURE:
+#ifdef HAVE_V4L2
+       case VIDIOC_PREVIEW:
+#endif
        {
-               struct video_tuner v;
-               if(copy_from_user(&v, arg, sizeof(v)))
-                       return -EFAULT;
-               /* Only one channel has a tuner */
-               if(v.tuner!=bttv_tvcards[btv->type].tuner)
-                       return -EINVAL;
-                               
-               if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC
-                  &&v.mode!=VIDEO_MODE_SECAM)
-                       return -EOPNOTSUPP;
-               bttv_call_i2c_clients(btv,cmd,&v);
-               if (btv->win.norm != v.mode) {
-                       btv->win.norm = v.mode;
-                        down(&btv->lock);
-                       set_pll(btv);
-                       make_vbitab(btv);
-                       spin_lock_irqsave(&btv->s_lock, irq_flags);
-                       bt848_set_winsize(btv);
-                       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-                       up(&btv->lock);
+               struct bttv_buffer *new;
+               int *on = arg;
+
+               if (*on) {
+                       /* verify args */
+                       if (NULL == btv->fbuf.base)
+                               return -EINVAL;
+                       if (fh->ov.width <48 ||
+                           fh->ov.height<32 ||
+                           fh->ov.width >bttv_tvnorms[btv->tvnorm].swidth  ||
+                           fh->ov.height>bttv_tvnorms[btv->tvnorm].sheight ||
+                           NULL == fh->ovfmt)
+                               return -EINVAL;
                }
+
+               if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
+                       return -EBUSY;
+               
+               down(&fh->lock);
+               if (*on) {
+                       fh->ov.tvnorm = btv->tvnorm;
+                       new = videobuf_alloc(sizeof(*new),
+                                            V4L2_BUF_TYPE_CAPTURE);
+                       bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
+               } else {
+                       new = NULL;
+               }
+
+               /* switch over */
+               retval = bttv_switch_overlay(btv,fh,new);
+               up(&fh->lock);
+               return retval;
+       }
+
+       case VIDIOCGMBUF:
+       {
+               struct video_mbuf *mbuf = arg;
+               int i;
+
+               if (!mmap)
+                       return -EINVAL;
+               down(&fh->lock);
+               retval = videobuf_mmap_setup
+                       (file,(struct videobuf_buffer**)fh->bufs,
+                        sizeof(struct bttv_buffer),
+                        gbuffers,gbufsize,V4L2_BUF_TYPE_CAPTURE,
+                        release_buffer);
+               if (retval < 0)
+                       goto fh_unlock_and_return;
+               memset(mbuf,0,sizeof(*mbuf));
+               mbuf->frames = gbuffers;
+               mbuf->size   = gbuffers * gbufsize;
+               for (i = 0; i < gbuffers; i++)
+                       mbuf->offsets[i] = i * gbufsize;
+               up(&fh->lock);
                return 0;
        }
-       case VIDIOCGPICT:
+       case VIDIOCMCAPTURE:
        {
-               struct video_picture p=btv->picture;
-               if(copy_to_user(arg, &p, sizeof(p)))
-                       return -EFAULT;
+               struct video_mmap *vm = arg;
+               struct bttv_buffer *buf;
+
+               if (vm->frame >= VIDEO_MAX_FRAME)
+                       return -EINVAL;
+
+               down(&fh->lock);
+               retval = -EINVAL;
+               buf = fh->bufs[vm->frame];
+               if (NULL == buf)
+                       goto fh_unlock_and_return;
+               if (0 == buf->vb.baddr)
+                       goto fh_unlock_and_return;
+               if (buf->vb.state == STATE_QUEUED ||
+                   buf->vb.state == STATE_ACTIVE)
+                       goto fh_unlock_and_return;
+               
+               retval = bttv_prepare_buffer(btv,buf,
+                                            format_by_palette(vm->format),
+                                            vm->width,vm->height,0);
+               if (0 != retval)
+                       goto fh_unlock_and_return;
+               bttv_queue_buffer(btv,buf);
+               up(&fh->lock);
                return 0;
        }
-       case VIDIOCSPICT:
+       case VIDIOCSYNC:
+       {
+               int *frame = arg;
+               struct bttv_buffer *buf;
+
+               if (*frame >= VIDEO_MAX_FRAME)
+                       return -EINVAL;
+
+               down(&fh->lock);
+               retval = -EINVAL;
+               buf = fh->bufs[*frame];
+               if (NULL == buf)
+                       goto fh_unlock_and_return;
+               retval = videobuf_waiton(&buf->vb,0,1);
+               if (0 != retval)
+                       goto fh_unlock_and_return;
+               switch (buf->vb.state) {
+               case STATE_ERROR:
+                       retval = -EIO;
+                       /* fall through */
+               case STATE_DONE:
+                       videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
+                       bttv_dma_free(btv,buf);
+                       break;
+               default:
+                       retval = -EINVAL;
+                       break;
+               }
+               up(&fh->lock);
+               return retval;
+       }
+
+        case BTTV_VERSION:
+        case VIDIOCGFREQ:
+        case VIDIOCSFREQ:
+        case VIDIOCGTUNER:
+        case VIDIOCSTUNER:
+        case VIDIOCGCHAN:
+        case VIDIOCSCHAN:
+       case VIDIOCGAUDIO:
+       case VIDIOCSAUDIO:
+               return bttv_common_ioctls(btv,cmd,arg);
+
+
+#ifdef HAVE_V4L2
+       /* ***  v4l2  *** ************************************************ */
+       case VIDIOC_QUERYCAP:
        {
-               struct video_picture p;
-               if(copy_from_user(&p, arg,sizeof(p)))
-                       return -EFAULT;
-               if (p.palette > PALETTEFMT_MAX)
+               struct v4l2_capability *cap = arg;
+
+               if (0 == v4l2)
                        return -EINVAL;
-               down(&btv->lock);
-               /* We want -128 to 127 we get 0-65535 */
-               bt848_bright(btv, (p.brightness>>8)-128);
-               /* 0-511 for the colour */
-               bt848_sat_u(btv, p.colour>>7);
-               bt848_sat_v(btv, ((p.colour>>7)*201L)/237);
-               /* -128 to 127 */
-               bt848_hue(btv, (p.hue>>8)-128);
-               /* 0-511 */
-               bt848_contrast(btv, p.contrast>>7);
-               btv->picture = p;
-               up(&btv->lock);
+                strcpy(cap->name,btv->video_dev.name);
+               cap->type = V4L2_TYPE_CAPTURE;
+               cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_PREVIEW
+                       | V4L2_FLAG_READ | V4L2_FLAG_SELECT;
+               if (mmap)
+                       cap->flags |= V4L2_FLAG_STREAMING;
+               cap->inputs = bttv_tvcards[btv->type].video_inputs;
+               cap->outputs = 0;
+               cap->audios = bttv_tvcards[btv->type].audio_inputs;
+               cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth;
+               cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight;
+               cap->minwidth = 48;
+               cap->minheight = 32;
+               cap->maxframerate = 30;
                return 0;
        }
-       case VIDIOCSWIN:
-       {
-               struct video_window vw;
-               struct video_clip *vcp = NULL;
-                       
-               if(copy_from_user(&vw,arg,sizeof(vw)))
-                       return -EFAULT;
 
-               down(&btv->lock);
-               if(vw.flags || vw.width < 16 || vw.height < 16) 
-               {
-                       spin_lock_irqsave(&btv->s_lock, irq_flags);
-                       btv->scr_on = 0;
-                       bt848_set_risc_jmps(btv,-1);
-                       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-                       up(&btv->lock);
-                       return -EINVAL;
-               }
-               if (btv->win.bpp < 4) 
-               {       /* adjust and align writes */
-                       vw.x = (vw.x + 3) & ~3;
-                       vw.width &= ~3;
+       case VIDIOC_ENUM_PIXFMT:
+       case VIDIOC_ENUM_FBUFFMT:
+       {
+               struct v4l2_fmtdesc *f = arg;
+               int i, index;
+
+               index = -1;
+               for (i = 0; i < BTTV_FORMATS; i++) {
+                       if (bttv_formats[i].fourcc != -1)
+                               index++;
+                       if (index == f->index)
+                               break;
                }
-               if (btv->needs_restart)
-                       bt848_restart(btv);
-               btv->win.x=vw.x;
-               btv->win.y=vw.y;
-               btv->win.width=vw.width;
-               btv->win.height=vw.height;
-               
-               spin_lock_irqsave(&btv->s_lock, irq_flags);
-               bt848_set_risc_jmps(btv,0);
-               bt848_set_winsize(btv);
-               spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-
-               /*
-                *      Do any clips.
-                */
-               if(vw.clipcount<0) {
-                       if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
-                               up(&btv->lock);
-                               return -ENOMEM;
-                       }
-                       if(copy_from_user(vcp, vw.clips,
-                                         VIDEO_CLIPMAP_SIZE)) {
-                               up(&btv->lock);
-                               vfree(vcp);
-                               return -EFAULT;
-                       }
-               } else if (vw.clipcount > 2048) {
-                       up(&btv->lock);
+               if (BTTV_FORMATS == i)
                        return -EINVAL;
-               } else if (vw.clipcount) {
-                       if((vcp=vmalloc(sizeof(struct video_clip)*
-                                       (vw.clipcount))) == NULL) {
-                               up(&btv->lock);
-                               return -ENOMEM;
-                       }
-                       if(copy_from_user(vcp,vw.clips,
-                                         sizeof(struct video_clip)*
-                                         vw.clipcount)) {
-                               up(&btv->lock);
-                               vfree(vcp);
-                               return -EFAULT;
-                       }
-               }
-               make_clip_tab(btv, vcp, vw.clipcount);
-               if (vw.clipcount != 0)
-                       vfree(vcp);
-               spin_lock_irqsave(&btv->s_lock, irq_flags);
-               bt848_set_risc_jmps(btv,-1);
-               spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-               up(&btv->lock);
+               if (cmd == VIDIOC_ENUM_FBUFFMT &&
+                   0 == (bttv_formats[i].flags & FORMAT_FLAGS_PACKED))
+                       return -EINVAL;
+               memset(f,0,sizeof(*f));
+               f->index = index;
+               strncpy(f->description,bttv_formats[i].name,31);
+               f->pixelformat = bttv_formats[i].fourcc;
+               f->depth = bttv_formats[i].depth;
                return 0;
        }
-       case VIDIOCGWIN:
+
+       case VIDIOC_G_FMT:
        {
-               struct video_window vw;
-               memset(&vw,0,sizeof(vw));
-               vw.x=btv->win.x;
-               vw.y=btv->win.y;
-               vw.width=btv->win.width;
-               vw.height=btv->win.height;
-               if(btv->win.interlace)
-                       vw.flags|=VIDEO_WINDOW_INTERLACE;
-               if(copy_to_user(arg,&vw,sizeof(vw)))
-                       return -EFAULT;
+               struct v4l2_format *f = arg;
+
+               memset(f,0,sizeof(*f));
+               f->type = V4L2_BUF_TYPE_CAPTURE;
+               f->fmt.pix.width        = fh->buf.vb.width;
+               f->fmt.pix.height       = fh->buf.vb.height;
+               f->fmt.pix.depth        = fh->buf.fmt->depth;
+               f->fmt.pix.pixelformat  = fh->buf.fmt->fourcc;
+               f->fmt.pix.sizeimage    =
+                       (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth)/8;
                return 0;
        }
-       case VIDIOCCAPTURE:
+       case VIDIOC_S_FMT:
        {
-               int v;
-               if(copy_from_user(&v, arg,sizeof(v)))
-                       return -EFAULT;
-               if(btv->win.vidadr == 0)
+               struct v4l2_format *f = arg;
+               const struct bttv_format *fmt;
+
+               if ((f->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+                       return -EINVAL;
+               fmt = format_by_fourcc(f->fmt.pix.pixelformat);
+               if (NULL == fmt)
+                       return -EINVAL;
+               if (f->fmt.pix.width  < 48 ||
+                   f->fmt.pix.height < 32)
                        return -EINVAL;
-               if (btv->win.width==0 || btv->win.height==0)
+               if (f->fmt.pix.flags & V4L2_FMT_FLAG_BYTESPERLINE)
+                       /* FIXME -- not implemented yet */
                        return -EINVAL;
-               if (1 == no_overlay)
-                       return -EIO;
-               spin_lock_irqsave(&btv->s_lock, irq_flags);
-               if (v == 1 && btv->win.vidadr != 0)
-                       btv->scr_on = 1;
-               if (v == 0)
-                       btv->scr_on = 0;
-               bt848_set_risc_jmps(btv,-1);
-               spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+
+               down(&fh->lock);
+               /* fixup format */
+               if (f->fmt.pix.width  > bttv_tvnorms[btv->tvnorm].swidth)
+                       f->fmt.pix.width = bttv_tvnorms[btv->tvnorm].swidth;
+               if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight)
+                       f->fmt.pix.height = bttv_tvnorms[btv->tvnorm].sheight;
+               if (!(f->fmt.pix.flags & V4L2_FMT_FLAG_INTERLACED) &&
+                   f->fmt.pix.height>bttv_tvnorms[btv->tvnorm].sheight/2)
+                       f->fmt.pix.height=bttv_tvnorms[btv->tvnorm].sheight/2;
+
+               if (f->fmt.pix.height > bttv_tvnorms[btv->tvnorm].sheight/2) {
+                       /* must interlace -- no field splitting available */
+                       f->fmt.pix.flags &= ~(V4L2_FMT_FLAG_TOPFIELD|
+                                             V4L2_FMT_FLAG_BOTFIELD);
+               } else {
+                       /* one field is enouth -- no interlace needed */
+                       f->fmt.pix.flags &= ~V4L2_FMT_FLAG_INTERLACED;
+               }
+               
+               /* update our state informations */
+               fh->buf.fmt             = fmt;
+               fh->buf.vb.width        = f->fmt.pix.width;
+               fh->buf.vb.height       = f->fmt.pix.height;
+               btv->init.buf.fmt       = fmt;
+               btv->init.buf.vb.width  = f->fmt.pix.width;
+               btv->init.buf.vb.height = f->fmt.pix.height;
+
+               /* update data for the application */
+               f->fmt.pix.depth = fmt->depth;
+               f->fmt.pix.sizeimage =
+                       (fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8;
+               up(&fh->lock);
                return 0;
        }
-       case VIDIOCGFBUF:
+
+       case VIDIOC_G_FBUF:
        {
-               struct video_buffer v;
-               v.base=(void *)btv->win.vidadr;
-               v.height=btv->win.sheight;
-               v.width=btv->win.swidth;
-               v.depth=btv->win.depth;
-               v.bytesperline=btv->win.bpl;
-               if(copy_to_user(arg, &v,sizeof(v)))
-                       return -EFAULT;
+               struct v4l2_framebuffer *fb = arg;
+
+               memset(fb,0,sizeof(*fb));
+               fb->base[0]    = btv->fbuf.base;
+               fb->fmt.width  = btv->fbuf.width;
+               fb->fmt.height = btv->fbuf.height;
+               fb->fmt.bytesperline = btv->fbuf.bytesperline;
+               fb->fmt.flags  = V4L2_FMT_FLAG_BYTESPERLINE;
+               fb->capability = V4L2_FBUF_CAP_CLIPPING;
+               if (fh->ovfmt) {
+                       fb->fmt.depth  = fh->ovfmt->depth;
+                       fb->fmt.pixelformat  = fh->ovfmt->fourcc;
+               }
                return 0;
-                       
        }
-       case VIDIOCSFBUF:
+       case VIDIOC_S_FBUF:
        {
-               struct video_buffer v;
+               struct v4l2_framebuffer *fb = arg;
+               const struct bttv_format *fmt;
+               unsigned long end;
+               
                if(!capable(CAP_SYS_ADMIN) &&
                   !capable(CAP_SYS_RAWIO))
                        return -EPERM;
-               if(copy_from_user(&v, arg,sizeof(v)))
-                       return -EFAULT;
-               if(v.depth!=8 && v.depth!=15 && v.depth!=16 && 
-                  v.depth!=24 && v.depth!=32 && v.width > 16 &&
-                  v.height > 16 && v.bytesperline > 16)
+
+               /* check args */
+               end = (unsigned long)fb->base[0] +
+                       fb->fmt.height * fb->fmt.bytesperline;
+               if (0 == find_videomem((unsigned long)fb->base[0],end))
                        return -EINVAL;
-               down(&btv->lock);
-               if (v.base)
-                       btv->win.vidadr=(unsigned long)v.base;
-               btv->win.sheight=v.height;
-               btv->win.swidth=v.width;
-               btv->win.bpp=((v.depth+7)&0x38)/8;
-               btv->win.depth=v.depth;
-               btv->win.bpl=v.bytesperline;
-
-#if 0 /* was broken for ages and nobody noticed.  Looks like we don't need
-        it any more as everybody explicitly sets the palette using VIDIOCSPICT
-        these days */
-               /* set sefault color format */
-               switch (v.depth) {
-               case  8: btv->picture.palette = VIDEO_PALETTE_HI240;  break;
-               case 15: btv->picture.palette = VIDEO_PALETTE_RGB555; break;
-               case 16: btv->picture.palette = VIDEO_PALETTE_RGB565; break;
-               case 24: btv->picture.palette = VIDEO_PALETTE_RGB24;  break;
-               case 32: btv->picture.palette = VIDEO_PALETTE_RGB32;  break;
+
+               fmt = format_by_fourcc(fb->fmt.pixelformat);
+               if (NULL == fmt)
+                       return -EINVAL;
+               if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
+                       return -EINVAL;
+
+               down(&fh->lock);
+               retval = -EINVAL;
+               if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+                       if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth)
+                               goto fh_unlock_and_return;
+                       if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight)
+                               goto fh_unlock_and_return;
                }
-#endif
-       
-               if (bttv_debug)
-                       printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
-                              v.base, v.width,v.height, btv->win.bpp, btv->win.bpl);
-               spin_lock_irqsave(&btv->s_lock, irq_flags);
-               bt848_set_winsize(btv);
-               spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-               up(&btv->lock);
-               return 0;               
+
+               /* ok, accept it */
+               btv->fbuf.base   = fb->base[0];
+               btv->fbuf.width  = fb->fmt.width;
+               btv->fbuf.height = fb->fmt.height;
+               btv->fbuf.depth  = fmt->depth;
+               if (fb->fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE)
+                       btv->fbuf.bytesperline = fb->fmt.bytesperline;
+               else
+                       btv->fbuf.bytesperline = btv->fbuf.width*fmt->depth/8;
+               
+               retval = 0;
+               fh->ovfmt = fmt;
+               btv->init.ovfmt = fmt;
+               if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
+                       fh->ov.x      = 0;
+                       fh->ov.y      = 0;
+                       fh->ov.width  = fb->fmt.width;
+                       fh->ov.height = fb->fmt.height;
+                       btv->init.ov.width  = fb->fmt.width;
+                       btv->init.ov.height = fb->fmt.height;
+                       if (fh->ov.clips)
+                               kfree(fh->ov.clips);
+                       fh->ov.clips = NULL;
+                       fh->ov.nclips = 0;
+
+                       if (check_btres(fh, RESOURCE_OVERLAY)) {
+                               struct bttv_buffer *new;
+               
+                               new = videobuf_alloc(sizeof(*new),
+                                                    V4L2_BUF_TYPE_CAPTURE);
+                               bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
+                               retval = bttv_switch_overlay(btv,fh,new);
+                       }
+               }
+               up(&fh->lock);
+               return retval;
        }
-       case VIDIOCKEY:
+       case VIDIOC_G_WIN:
        {
-               /* Will be handled higher up .. */
+               struct v4l2_window *win = arg;
+
+               memset(win,0,sizeof(*win));
+               win->x      = fh->ov.x;
+               win->y      = fh->ov.y;
+               win->width  = fh->ov.width;
+               win->height = fh->ov.height;
                return 0;
        }
-       case VIDIOCGFREQ:
+       case VIDIOC_S_WIN:
        {
-               unsigned long v=btv->win.freq;
-               if(copy_to_user(arg,&v,sizeof(v)))
-                       return -EFAULT;
-               return 0;
+               struct v4l2_window *win = arg;
+
+               return setup_window(fh,btv,win->x,win->y,
+                                   win->width,win->height,
+                                   (struct video_clip*)win->clips,
+                                   win->clipcount);
        }
-       case VIDIOCSFREQ:
+
+       case VIDIOC_REQBUFS:
        {
-               unsigned long v;
-               if(copy_from_user(&v, arg, sizeof(v)))
-                       return -EFAULT;
-               btv->win.freq=v;
-               bttv_call_i2c_clients(btv,cmd,&v);
-#if 1
-               if (btv->radio && btv->has_matchbox)
-                       tea5757_set_freq(btv,v);
-#endif
+               struct v4l2_requestbuffers *req = arg;
+               int size,count;
+
+               if (!mmap)
+                       return -EINVAL;
+               if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+                       return -EINVAL;
+               if (req->count < 0)
+                       return -EINVAL;
+
+               down(&fh->lock);
+               retval = -EINVAL;
+               if (NULL == fh->buf.fmt    ||
+                   0 == fh->buf.vb.width  ||
+                   0 == fh->buf.vb.height)
+                       goto fh_unlock_and_return;
+
+               size = (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth) >> 3;
+               size = (size + PAGE_SIZE - 1) & PAGE_MASK;
+               count = req->count;
+               if (count > VIDEO_MAX_FRAME)
+                       count = VIDEO_MAX_FRAME;
+               while (size * count > gbuffers * gbufsize)
+                       count--;
+               retval = videobuf_mmap_setup(file,
+                                            (struct videobuf_buffer**)fh->bufs,
+                                            sizeof(struct bttv_buffer),
+                                            count,size,V4L2_BUF_TYPE_CAPTURE,
+                                            release_buffer);
+               if (retval < 0)
+                       goto fh_unlock_and_return;
+               req->type  = V4L2_BUF_TYPE_CAPTURE;
+               req->count = count;
+               up(&fh->lock);
                return 0;
        }
-       
-       case VIDIOCGAUDIO:
+       case VIDIOC_QUERYBUF:
        {
-               struct video_audio v;
-
-               v=btv->audio_dev;
-               v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
-               v.flags|=VIDEO_AUDIO_MUTABLE;
-               strcpy(v.name,"TV");
-
-               v.mode = VIDEO_SOUND_MONO;
-               bttv_call_i2c_clients(btv,cmd,&v);
-
-               /* card specific hooks */
-               if (bttv_tvcards[btv->type].audio_hook)
-                       bttv_tvcards[btv->type].audio_hook(btv,&v,0);
+               struct v4l2_buffer *b = arg;
 
-               if(copy_to_user(arg,&v,sizeof(v)))
-                       return -EFAULT;
+               if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+                       return -EINVAL;
+               if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
+                       return -EINVAL;
+               if (NULL == fh->bufs[b->index])
+                       return -EINVAL;
+               videobuf_status(b,&fh->bufs[b->index]->vb);
                return 0;
        }
-       case VIDIOCSAUDIO:
+       case VIDIOC_QBUF:
        {
-               struct video_audio v;
+               struct v4l2_buffer *b = arg;
+               struct bttv_buffer *buf;
+               int field = 0;
 
-               if(copy_from_user(&v,arg, sizeof(v)))
-                       return -EFAULT;
-               down(&btv->lock);
-               if(v.flags&VIDEO_AUDIO_MUTE)
-                       audio(btv, AUDIO_MUTE, 1);
-               /* One audio source per tuner -- huh? <GA> */
-               if(v.audio<0 || v.audio >= bttv_tvcards[btv->type].audio_inputs) {
-                       up(&btv->lock);
+               if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
+                       return -EINVAL;
+               if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
                        return -EINVAL;
-               }
-               /* bt848_muxsel(btv,v.audio); */
-               if(!(v.flags&VIDEO_AUDIO_MUTE))
-                       audio(btv, AUDIO_UNMUTE, 1);
-
-               bttv_call_i2c_clients(btv,cmd,&v);
-               
-               /* card specific hooks */
-               if (bttv_tvcards[btv->type].audio_hook)
-                       bttv_tvcards[btv->type].audio_hook(btv,&v,1);
 
-               btv->audio_dev=v;
-               up(&btv->lock);
+               down(&fh->lock);
+               retval = -EINVAL;
+               buf = fh->bufs[b->index];
+               if (NULL == buf)
+                       goto fh_unlock_and_return;
+               if (0 == buf->vb.baddr)
+                       goto fh_unlock_and_return;
+               if (buf->vb.state == STATE_QUEUED ||
+                   buf->vb.state == STATE_ACTIVE)
+                       goto fh_unlock_and_return;
+
+               if (b->flags & V4L2_BUF_FLAG_TOPFIELD)
+                       field |= VBUF_FIELD_ODD;
+               if (b->flags & V4L2_BUF_FLAG_BOTFIELD)
+                       field |= VBUF_FIELD_EVEN;
+               retval = bttv_prepare_buffer(btv,buf,fh->buf.fmt,
+                                            fh->buf.vb.width,fh->buf.vb.height,field);
+               if (0 != retval)
+                       goto fh_unlock_and_return;
+
+               list_add_tail(&buf->vb.stream,&fh->stream);
+               if (check_btres(fh, RESOURCE_STREAMING))
+                       bttv_queue_buffer(btv,buf);
+               up(&fh->lock);
                return 0;
        }
-
-       case VIDIOCSYNC:
+       case VIDIOC_DQBUF:
        {
-               DECLARE_WAITQUEUE(wait, current);
+               struct v4l2_buffer *b = arg;
+               struct bttv_buffer *buf;
 
-               if(copy_from_user((void *)&i,arg,sizeof(int)))
-                       return -EFAULT;
-               if (i < 0 || i >= gbuffers)
+               if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
                        return -EINVAL;
-               switch (btv->gbuf[i].stat) {
-               case GBUFFER_UNUSED:
-                       ret = -EINVAL;
+
+               down(&fh->lock);
+               retval = -EINVAL;
+               if (list_empty(&fh->stream))
+                       goto fh_unlock_and_return;
+               buf = list_entry(fh->stream.next, struct bttv_buffer, vb.stream);
+               retval = videobuf_waiton(&buf->vb,0,1);
+               if (retval < 0)
+                       goto fh_unlock_and_return;
+               switch (buf->vb.state) {
+               case STATE_ERROR:
+                       retval = -EIO;
+                       /* fall through */
+               case STATE_DONE:
+                       videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
+                       buf->vb.state = STATE_IDLE;
                        break;
-               case GBUFFER_GRABBING:
-                       add_wait_queue(&btv->capq, &wait);
-                       current->state = TASK_INTERRUPTIBLE;
-                       while(btv->gbuf[i].stat==GBUFFER_GRABBING) {
-                               if (bttv_debug)
-                                       printk("bttv%d: cap sync: sleep on %d\n",btv->nr,i);
-                               schedule();
-                               if(signal_pending(current)) {
-                                       remove_wait_queue(&btv->capq, &wait);
-                                       current->state = TASK_RUNNING;
-                                       return -EINTR;
-                               }
-                       }
-                       remove_wait_queue(&btv->capq, &wait);
-                       current->state = TASK_RUNNING;
-                       /* fall throuth */
-               case GBUFFER_DONE:
-               case GBUFFER_ERROR:
-                       ret = (btv->gbuf[i].stat == GBUFFER_ERROR) ? -EIO : 0;
-                       if (bttv_debug)
-                               printk("bttv%d: cap sync: buffer %d, retval %d\n",btv->nr,i,ret);
-                       btv->gbuf[i].stat = GBUFFER_UNUSED;
-               }
-               if (btv->needs_restart) {
-                       down(&btv->lock);
-                       bt848_restart(btv);
-                       up(&btv->lock);
+               default:
+                       retval = -EINVAL;
+                       goto fh_unlock_and_return;
                }
-               return ret;
-       }
-
-       case BTTV_FIELDNR: 
-               if(copy_to_user((void *) arg, (void *) &btv->last_field, 
-                               sizeof(btv->last_field)))
-                       return -EFAULT;
-               break;
-      
-       case BTTV_PLLSET: {
-               struct bttv_pll_info p;
-               if(!capable(CAP_SYS_ADMIN))
-                       return -EPERM;
-               if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
-                       return -EFAULT;
-               down(&btv->lock);
-               btv->pll.pll_ifreq = p.pll_ifreq;
-               btv->pll.pll_ofreq = p.pll_ofreq;
-               btv->pll.pll_crystal = p.pll_crystal;
-               up(&btv->lock);
-               break;
-       }
-
-       case VIDIOCMCAPTURE:
-       {
-               struct video_mmap vm;
-               int ret;
-               if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
-                       return -EFAULT;
-               down(&btv->lock);
-               ret = vgrab(btv, &vm);
-               up(&btv->lock);
-               return ret;
+               list_del(&buf->vb.stream);
+               memset(b,0,sizeof(*b));
+               videobuf_status(b,&buf->vb);
+               up(&fh->lock);
+               return retval;
        }
-               
-       case VIDIOCGMBUF:
+       case VIDIOC_STREAMON:
        {
-               struct video_mbuf vm;
-               memset(&vm, 0 , sizeof(vm));
-               vm.size=gbufsize*gbuffers;
-               vm.frames=gbuffers;
-               for (i = 0; i < gbuffers; i++)
-                       vm.offsets[i]=i*gbufsize;
-               if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
-                       return -EFAULT;
-               return 0;
-       }
+               struct list_head *list;
+               struct bttv_buffer *buf;
                
-       case VIDIOCGUNIT:
-       {
-               struct video_unit vu;
-               vu.video=btv->video_dev.minor;
-               vu.vbi=btv->vbi_dev.minor;
-               if(btv->radio_dev.minor!=-1)
-                       vu.radio=btv->radio_dev.minor;
-               else
-                       vu.radio=VIDEO_NO_UNIT;
-               vu.audio=VIDEO_NO_UNIT;
-               vu.teletext=VIDEO_NO_UNIT;
-               if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
-                       return -EFAULT;
+               if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING))
+                       return -EBUSY;
+               bttv_field_count(btv);
+               down(&fh->lock);
+               list_for_each(list,&fh->stream) {
+                       buf = list_entry(list, struct bttv_buffer, vb.stream);
+                       if (buf->vb.state == STATE_PREPARED)
+                               bttv_queue_buffer(btv,buf);
+               }
+               up(&fh->lock);
                return 0;
        }
-               
-       case BTTV_BURST_ON:
+       case VIDIOC_STREAMOFF:
        {
-               burst(1);
+               down(&fh->lock);
+               retval = -EINVAL;
+               if (!check_btres(fh, RESOURCE_STREAMING))
+                       goto fh_unlock_and_return;
+               bttv_stop_streaming(btv,fh);
+               up(&fh->lock);
                return 0;
        }
 
-       case BTTV_BURST_OFF:
+       case VIDIOC_QUERYCTRL:
        {
-               burst(0);
+               struct v4l2_queryctrl *c = arg;
+               int i;
+
+               v4l2_fill_ctrl_category(c);
+               if ((c->id <  V4L2_CID_BASE ||
+                    c->id >= V4L2_CID_LASTP1) &&
+                   (c->id <  V4L2_CID_PRIVATE_BASE ||
+                    c->id >= V4L2_CID_PRIVATE_LASTP1))
+                       return -EINVAL;
+               for (i = 0; i < BTTV_CTLS; i++)
+                       if (bttv_ctls[i].id == c->id)
+                               break;
+               if (i == BTTV_CTLS) {
+                       *c = no_ctl;
+                       return 0;
+               }
+               *c = bttv_ctls[i];
+               if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
+                       struct video_audio va;
+                       memset(&va,0,sizeof(va));
+                       bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
+                       if (bttv_tvcards[btv->type].audio_hook)
+                               bttv_tvcards[btv->type].audio_hook(btv,&va,0);
+                       switch (bttv_ctls[i].id) {
+                       case V4L2_CID_AUDIO_VOLUME:
+                               if (!(va.flags & VIDEO_AUDIO_VOLUME))
+                                       *c = no_ctl;
+                               break;
+                       case V4L2_CID_AUDIO_BALANCE:
+                               if (!(va.flags & VIDEO_AUDIO_BALANCE))
+                                       *c = no_ctl;
+                               break;
+                       case V4L2_CID_AUDIO_BASS:
+                               if (!(va.flags & VIDEO_AUDIO_BASS))
+                                       *c = no_ctl;
+                               break;
+                       case V4L2_CID_AUDIO_TREBLE:
+                               if (!(va.flags & VIDEO_AUDIO_TREBLE))
+                                       *c = no_ctl;
+                               break;
+                       }
+               }
                return 0;
        }
-
-       case BTTV_VERSION:
-       {
-               return BTTV_VERSION_CODE;
-       }
-                        
-       case BTTV_PICNR:
+       case VIDIOC_G_CTRL:
+               return get_control(btv,arg);
+       case VIDIOC_S_CTRL:
+               return set_control(btv,arg);
+       case VIDIOC_G_PARM:
        {
-               /* return picture;*/
-               return  0;
+               struct v4l2_streamparm *parm = arg;
+               struct v4l2_standard s;
+               if (parm->type != V4L2_BUF_TYPE_CAPTURE)
+                       return -EINVAL;
+               memset(parm,0,sizeof(*parm));
+               v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, 0);
+               parm->parm.capture.timeperframe = v4l2_video_std_tpf(&s);
+               return 0;
        }
+       
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_ENUMINPUT:
+       case VIDIOC_G_INPUT:
+       case VIDIOC_S_INPUT:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_FREQ:
+       case VIDIOC_S_FREQ:
+               return bttv_common_ioctls(btv,cmd,arg);
+#endif /* HAVE_V4L2 */
 
        default:
                return -ENOIOCTLCMD;
        }
        return 0;
-}
 
-/*
- *     This maps the vmalloced and reserved fbuffer to user space.
- *
- *  FIXME: 
- *  - PAGE_READONLY should suffice!?
- *  - remap_page_range is kind of inefficient for page by page remapping.
- *    But e.g. pte_alloc() does not work in modules ... :-(
- */
+ fh_unlock_and_return:
+       up(&fh->lock);
+       return retval;
+}
 
-static int do_bttv_mmap(struct vm_area_struct *vma, struct bttv *btv, const char *adr, unsigned long size)
+/* start capture to a kernel bounce buffer */
+static int bttv_read_capture(struct bttv_fh *fh)
 {
-        unsigned long start=(unsigned long) adr;
-        unsigned long page,pos;
-
-        if (size>gbuffers*gbufsize)
-                return -EINVAL;
-        if (!btv->fbuffer) {
-                if(fbuffer_alloc(btv))
-                        return -EINVAL;
-        }
-        pos=(unsigned long) btv->fbuffer;
-        while (size > 0) {
-                page = kvirt_to_pa(pos);
-                if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
-                        return -EAGAIN;
-                start+=PAGE_SIZE;
-                pos+=PAGE_SIZE;
-                size-=PAGE_SIZE;
-        }
-        return 0;
+       struct bttv *btv = fh->btv;
+       int rc;
+       
+       dprintk("bttv%d: read start\n",btv->nr);
+       rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,
+                                fh->buf.vb.width,fh->buf.vb.height,0);
+       if (0 != rc)
+               return rc;
+
+       bttv_queue_buffer(btv,&fh->read_buf);
+       fh->read_off = 0;
+       return 0;
 }
 
-static int bttv_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size)
+/*
+ * blocking read for a complete video frame
+ * => no kernel bounce buffer needed.
+ */
+static ssize_t
+bttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv,
+                  char *data,size_t count, loff_t *ppos)
 {
-        struct bttv *btv=(struct bttv *)dev;
-        int r;
-
-        down(&btv->lock);
-        r=do_bttv_mmap(vma, btv, adr, size);
-        up(&btv->lock);
-        return r;
+        int rc;
+
+        /* setup stuff */
+       dprintk("bttv%d: read zerocopy\n",btv->nr);
+        fh->read_buf.vb.baddr = (unsigned long)data;
+        fh->read_buf.vb.bsize = count;
+        rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,
+                                fh->buf.vb.width,fh->buf.vb.height,0);
+        if (0 != rc)
+               goto done;
+       
+        /* start capture & wait */
+        bttv_queue_buffer(btv,&fh->read_buf);
+        rc = videobuf_waiton(&fh->read_buf.vb,0,1);
+        if (0 == rc) {
+               videobuf_dma_pci_sync(btv->dev,&fh->read_buf.vb.dma);
+                rc = fh->read_buf.vb.size;
+       }
+
+ done:
+       /* cleanup */
+       bttv_dma_free(btv,&fh->read_buf);
+       fh->read_buf.vb.baddr = 0;
+       fh->read_buf.vb.size = 0;
+       return rc;
 }
 
 
-static struct video_device bttv_template=
+static ssize_t bttv_read(struct file *file, char *data,
+                        size_t count, loff_t *ppos)
 {
-       owner:          THIS_MODULE,
-       name:           "UNSET",
-       type:           VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
-       hardware:       VID_HARDWARE_BT848,
-       open:           bttv_open,
-       close:          bttv_close,
-       read:           bttv_read,
-       write:          bttv_write,
-       ioctl:          bttv_ioctl,
-       mmap:           bttv_mmap,
-       minor:          -1,
-};
-
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+       int rc, bytes, size;
 
-static long vbi_read(struct video_device *v, char *buf, unsigned long count,
-                    int nonblock)
-{
-       struct bttv *btv=(struct bttv *)(v-2);
-       int q,todo;
-       DECLARE_WAITQUEUE(wait, current);
+       if (btv->errors)
+               bttv_reinit_bt848(btv);
+       if (locked_btres(btv,RESOURCE_STREAMING))
+               return -EBUSY;
 
-       todo=count;
-       while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) 
-       {
-               if (btv->needs_restart) {
-                       down(&btv->lock);
-                       bt848_restart(btv);
-                       up(&btv->lock);
-               }
-               if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
-                       return -EFAULT;
-               todo-=q;
-               buf+=q;
+       down(&fh->lock);
+       size = fh->buf.fmt->depth*fh->buf.vb.width*fh->buf.vb.height >> 3;
+       if (-1 == fh->read_off  &&  count >= size  &&
+           !(file->f_flags & O_NONBLOCK)) {
+               rc = bttv_read_zerocopy(fh,btv,data,count,ppos);
+               if (rc >= 0)
+                       /* ok, all done */
+                       goto unlock_out;
+               /* fallback to kernel bounce buffer on failures */
+       }
+       
+       if (-1 == fh->read_off) {
+               /* need to capture a new frame */
+               rc = bttv_read_capture(fh);
+               if (0 != rc)
+                       goto unlock_out;
+       }
+
+       /* wait until capture is done */
+        rc = videobuf_waiton(&fh->read_buf.vb, file->f_flags & O_NONBLOCK,1);
+       if (0 != rc)
+               goto unlock_out;
+
+       /* copy to userspace */
+       bytes = count;
+       if (bytes > fh->read_buf.vb.size - fh->read_off)
+               bytes = fh->read_buf.vb.size - fh->read_off;
+       rc = -EFAULT;
+       if (copy_to_user(data,fh->read_buf.vb.dma.vmalloc +
+                        fh->read_off,bytes))
+               goto unlock_out;
+
+       rc = bytes;
+       fh->read_off += bytes;
+       dprintk("bttv%d: read %d bytes\n",btv->nr,bytes);
+       if (fh->read_off == fh->read_buf.vb.size) {
+               /* all data copied, cleanup */
+               dprintk("bttv%d: read done\n",btv->nr);
+               bttv_dma_free(btv,&fh->read_buf);
+               fh->read_off = -1;
+       }
+
+ unlock_out:
+       up(&fh->lock);
+       return rc;
+}
 
-               add_wait_queue(&btv->vbiq, &wait);
-               current->state = TASK_INTERRUPTIBLE;
-               if (todo && q==VBIBUF_SIZE-btv->vbip) 
-               {
-                       if(nonblock)
-                       {
-                               remove_wait_queue(&btv->vbiq, &wait);
-                               current->state = TASK_RUNNING;
-                               if(count==todo)
-                                       return -EWOULDBLOCK;
-                               return count-todo;
-                       }
-                       schedule();
-                       if(signal_pending(current))
-                       {
-                               remove_wait_queue(&btv->vbiq, &wait);
-                                current->state = TASK_RUNNING;
-                               if(todo==count)
-                                       return -EINTR;
-                               else
-                                       return count-todo;
+static unsigned int bttv_poll(struct file *file, poll_table *wait)
+{
+       struct bttv_fh *fh = file->private_data;
+       struct bttv_buffer *buf;
+
+       if (check_alloc_btres(fh->btv,fh,RESOURCE_STREAMING)) {
+               /* streaming capture */
+               if (list_empty(&fh->stream))
+                       return POLLERR;
+               buf = list_entry(fh->stream.next,struct bttv_buffer,vb.stream);
+       } else {
+               /* read() capture */
+               down(&fh->lock);
+               if (-1 == fh->read_off) {
+                       /* need to capture a new frame */
+                       if (locked_btres(fh->btv,RESOURCE_STREAMING) ||
+                           0 != bttv_read_capture(fh)) {
+                               up(&fh->lock);
+                               return POLLERR;
                        }
                }
-               remove_wait_queue(&btv->vbiq, &wait);
-               current->state = TASK_RUNNING;
-       }
-       if (todo) 
-       {
-               if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
-                       return -EFAULT;
-               btv->vbip+=todo;
+               up(&fh->lock);
+               buf = &fh->read_buf;
        }
-       return count;
+       
+       poll_wait(file, &buf->vb.done, wait);
+       if (buf->vb.state == STATE_DONE ||
+           buf->vb.state == STATE_ERROR)
+               return POLLIN|POLLRDNORM;
+       return 0;
 }
 
-static unsigned int vbi_poll(struct video_device *dev, struct file *file,
-       poll_table *wait)
+static int bttv_open(struct inode *inode, struct file *file)
 {
-       struct bttv *btv=(struct bttv *)(dev-2);
-       unsigned int mask = 0;
-
-       poll_wait(file, &btv->vbiq, wait);
+       unsigned int minor = minor(inode->i_rdev);
+       struct bttv *btv = NULL;
+       struct bttv_fh *fh;
+       int i;
 
-       if (btv->vbip < VBIBUF_SIZE)
-               mask |= (POLLIN | POLLRDNORM);
+       dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
 
-       return mask;
-}
+       for (i = 0; i < bttv_num; i++) {
+               if (bttvs[i].video_dev.minor == minor) {
+                       btv = &bttvs[i];
+                       break;
+               }
+       }
+       if (NULL == btv)
+               return -ENODEV;
 
-static int vbi_open(struct video_device *dev, int flags)
-{
-       struct bttv *btv=(struct bttv *)(dev-2);
-       unsigned long irq_flags;
+       dprintk(KERN_DEBUG "bttv%d: open called (video)\n",btv->nr);
 
-        down(&btv->lock);
-       if (btv->needs_restart)
-               bt848_restart(btv);
-       set_pll(btv);
-       btv->vbip=VBIBUF_SIZE;
-       spin_lock_irqsave(&btv->s_lock, irq_flags);
-       btv->vbi_on = 1;
-       bt848_set_risc_jmps(btv,-1);
-       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-       up(&btv->lock);
+       /* allocate per filehandle data */
+       fh = kmalloc(sizeof(*fh),GFP_KERNEL);
+       if (NULL == fh)
+               return -ENOMEM;
+       file->private_data = fh;
+       *fh = btv->init;
+        init_MUTEX(&fh->lock);
+       INIT_LIST_HEAD(&fh->stream);
+       init_waitqueue_head(&fh->read_buf.vb.done);
+       fh->read_off = -1;
 
-       return 0;   
+       i2c_vidiocschan(btv);
+        return 0;
 }
 
-static void vbi_close(struct video_device *dev)
+static int bttv_release(struct inode *inode, struct file *file)
 {
-       struct bttv *btv=(struct bttv *)(dev-2);
-       unsigned long irq_flags;
-
-       spin_lock_irqsave(&btv->s_lock, irq_flags);
-       btv->vbi_on = 0;
-       bt848_set_risc_jmps(btv,-1);
-       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
+       struct bttv_fh *fh = file->private_data;
+       struct bttv *btv = fh->btv;
+
+       /* turn off overlay, stop outstanding captures */
+       if (check_btres(fh, RESOURCE_OVERLAY))
+               bttv_switch_overlay(btv,fh,NULL);
+       if (check_btres(fh, RESOURCE_STREAMING))
+               bttv_stop_streaming(btv,fh);
+       bttv_dma_free(btv,&fh->read_buf);
+
+       file->private_data = NULL;
+       kfree(fh);
+       return 0;
 }
 
-static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+static int
+bttv_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       struct bttv *btv=(struct bttv *)(dev-2);
+       struct bttv_fh *fh = file->private_data;
+       int err;
 
-       switch (cmd) {  
-       case VIDIOCGCAP:
-       {
-               struct video_capability b;
-               strcpy(b.name,btv->vbi_dev.name);
-               b.type = ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) |
-                       VID_TYPE_TELETEXT;
-               b.channels = 0;
-               b.audios = 0;
-               b.maxwidth = 0;
-               b.maxheight = 0;
-               b.minwidth = 0;
-               b.minheight = 0;
-               if(copy_to_user(arg,&b,sizeof(b)))
-                       return -EFAULT;
-               return 0;
-       }
-       case VIDIOCGFREQ:
-       case VIDIOCSFREQ:
-       case VIDIOCGTUNER:
-       case VIDIOCSTUNER:
-       case VIDIOCGCHAN:
-       case VIDIOCSCHAN:
-       case BTTV_VERSION:
-               return bttv_ioctl(dev-2,cmd,arg);
-       case BTTV_VBISIZE:
-               /* make alevt happy :-) */
-               return VBIBUF_SIZE;
-       default:
+       if (!mmap)
                return -EINVAL;
-       }
+       down(&fh->lock);
+       err = videobuf_mmap_mapper(vma,(struct videobuf_buffer**)fh->bufs);
+       up(&fh->lock);
+       return err;
 }
 
-static struct video_device vbi_template=
+static struct file_operations bttv_fops =
+{
+       owner:    THIS_MODULE,
+       open:     bttv_open,
+       release:  bttv_release,
+       ioctl:    video_generic_ioctl,
+       llseek:   no_llseek,
+       read:     bttv_read,
+       mmap:     bttv_mmap,
+       poll:     bttv_poll,
+};
+
+static struct video_device bttv_template =
 {
-       owner:          THIS_MODULE,
-       name:           "bttv vbi",
-       type:           VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
-       hardware:       VID_HARDWARE_BT848,
-       open:           vbi_open,
-       close:          vbi_close,
-       read:           vbi_read,
-       write:          bttv_write,
-       poll:           vbi_poll,
-       ioctl:          vbi_ioctl,
-       minor:          -1,
+       name:     "UNSET",
+       type:     VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY|
+                 VID_TYPE_CLIPPING|VID_TYPE_SCALES,
+       hardware: VID_HARDWARE_BT848,
+       fops:     &bttv_fops,
+       kernel_ioctl: bttv_ioctl,
+       minor:    -1,
 };
 
+/* ----------------------------------------------------------------------- */
+/* radio interface                                                         */
 
-static int radio_open(struct video_device *dev, int flags)
+static int radio_open(struct inode *inode, struct file *file)
 {
-       struct bttv *btv = (struct bttv *)(dev-1);
-       unsigned long v;
+       unsigned int minor = minor(inode->i_rdev);
+       struct bttv *btv = NULL;
+       unsigned long v = 400*16;
+       int i;
 
-        down(&btv->lock);
-       if (btv->user)
-               goto busy_unlock;
-       btv->user++;
+       dprintk("bttv: open minor=%d\n",minor);
 
-       btv->radio = 1;
-       v = 400*16;
-       bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v);
-       bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
-       bt848_muxsel(btv,0);
-       up(&btv->lock);
+       for (i = 0; i < bttv_num; i++) {
+               if (bttvs[i].radio_dev.minor == minor) {
+                       btv = &bttvs[i];
+                       break;
+               }
+       }
+       if (NULL == btv)
+               return -ENODEV;
+
+       dprintk("bttv%d: open called (radio)\n",btv->nr);
+       down(&btv->lock);
+       if (btv->radio_user) {
+               up(&btv->lock);
+               return -EBUSY;
+       }
+       btv->radio_user++;
+       file->private_data = btv;
 
-       return 0;   
+       i2c_vidiocschan(btv);
+       bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v);
+        bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
+       audio_mux(btv,AUDIO_RADIO);
 
- busy_unlock:
        up(&btv->lock);
-       return -EBUSY;
+        return 0;
 }
 
-static void radio_close(struct video_device *dev)
+static int radio_release(struct inode *inode, struct file *file)
 {
-       struct bttv *btv=(struct bttv *)(dev-1);
+       struct bttv    *btv = file->private_data;
 
-       down(&btv->lock);
-       btv->user--;
-       btv->radio = 0;
-       up(&btv->lock);
+       btv->radio_user--;
+       return 0;
 }
 
-static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+static int radio_ioctl(struct inode *inode, struct file *file,
+                      unsigned int cmd, void *arg)
 {
-       return -EINVAL;
-}
+       struct bttv    *btv = file->private_data;
 
-static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
-{
-        struct bttv *btv=(struct bttv *)(dev-1);
-       switch (cmd) {  
+       switch (cmd) {
        case VIDIOCGCAP:
        {
-               struct video_capability v;
-               strcpy(v.name,btv->video_dev.name);
-               v.type = VID_TYPE_TUNER;
-               v.channels = 1;
-               v.audios = 1;
-               /* No we don't do pictures */
-               v.maxwidth = 0;
-               v.maxheight = 0;
-               v.minwidth = 0;
-               v.minheight = 0;
-               if (copy_to_user(arg, &v, sizeof(v)))
-                       return -EFAULT;
-               return 0;
-               break;
-       }
-       case VIDIOCGTUNER:
-       {
-               struct video_tuner v;
-               if(copy_from_user(&v,arg,sizeof(v))!=0)
-                       return -EFAULT;
-               if(v.tuner||btv->channel)       /* Only tuner 0 */
-                       return -EINVAL;
-               strcpy(v.name, "Radio");
-               /* japan:          76.0 MHz -  89.9 MHz
-                  western europe: 87.5 MHz - 108.0 MHz
-                  russia:         65.0 MHz - 108.0 MHz */
-               v.rangelow=(int)(65*16);
-               v.rangehigh=(int)(108*16);
-               v.flags= 0; /* XXX */
-               v.mode = 0; /* XXX */
-               bttv_call_i2c_clients(btv,cmd,&v);
-               if(copy_to_user(arg,&v,sizeof(v)))
-                       return -EFAULT;
-               return 0;
+                struct video_capability *cap = arg;
+
+               memset(cap,0,sizeof(*cap));
+                strcpy(cap->name,btv->radio_dev.name);
+                cap->type = VID_TYPE_TUNER;
+               cap->channels = 1;
+               cap->audios = 1;
+                return 0;
        }
-       case VIDIOCSTUNER:
-       {
-               struct video_tuner v;
-               if(copy_from_user(&v, arg, sizeof(v)))
-                       return -EFAULT;
-               /* Only channel 0 has a tuner */
-               if(v.tuner!=0 || btv->channel)
-                       return -EINVAL;
-               /* XXX anything to do ??? */
+
+        case VIDIOCGTUNER:
+        {
+                struct video_tuner *v = arg;
+
+                if(v->tuner)
+                        return -EINVAL;
+               memset(v,0,sizeof(*v));
+                strcpy(v->name, "Radio");
+                /* japan:          76.0 MHz -  89.9 MHz
+                   western europe: 87.5 MHz - 108.0 MHz
+                   russia:         65.0 MHz - 108.0 MHz */
+                v->rangelow=(int)(65*16);
+                v->rangehigh=(int)(108*16);
+                bttv_call_i2c_clients(btv,cmd,v);
+                return 0;
+        }
+        case VIDIOCSTUNER:
+               /* nothing to do */
                return 0;
-       }
-       case VIDIOCGFREQ:
-       case VIDIOCSFREQ:
+       
+       case BTTV_VERSION:
+        case VIDIOCGFREQ:
+        case VIDIOCSFREQ:
        case VIDIOCGAUDIO:
        case VIDIOCSAUDIO:
-               bttv_ioctl((struct video_device *)btv,cmd,arg);
-               break;
+               return bttv_common_ioctls(btv,cmd,arg);
+
        default:
                return -ENOIOCTLCMD;
        }
        return 0;
 }
 
-static struct video_device radio_template=
+static struct file_operations radio_fops =
 {
-       owner:          THIS_MODULE,
-       name:           "bttv radio",
-       type:           VID_TYPE_TUNER,
-       hardware:       VID_HARDWARE_BT848,
-       open:           radio_open,
-       close:          radio_close,
-       read:           radio_read,          /* just returns -EINVAL */
-       write:          bttv_write,          /* just returns -EINVAL */
-       ioctl:          radio_ioctl,
-       minor:          -1,
+       owner:    THIS_MODULE,
+       open:     radio_open,
+       release:  radio_release,
+       ioctl:    video_generic_ioctl,
+       llseek:   no_llseek,
 };
 
-
-static void bt848_set_risc_jmps(struct bttv *btv, int flags)
+static struct video_device radio_template =
 {
-       if (-1 == flags) {
-               /* defaults */
-               flags = 0;
-               if (btv->scr_on)
-                       flags |= 0x03;
-               if (btv->vbi_on)
-                       flags |= 0x0c;
-       }
-
-       if (bttv_debug > 1)
-               printk("bttv%d: set_risc_jmp %08lx:",
-                      btv->nr,virt_to_bus(btv->risc_jmp));
-
-       /* Sync to start of odd field */
-       btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC
-                                |BT848_FIFO_STATUS_VRE);
-       btv->risc_jmp[1]=cpu_to_le32(0);
-
-       /* Jump to odd vbi sub */
-       btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20));
-       if (flags&8) {
-               if (bttv_debug > 1)
-                       printk(" ev=%08lx",virt_to_bus(btv->vbi_odd));
-               btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd));
-       } else {
-               if (bttv_debug > 1)
-                       printk(" -----------");
-               btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->risc_jmp+4));
-       }
-
-        /* Jump to odd sub */
-       btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20));
-       if (0 != btv->risc_cap_odd) {
-               if (bttv_debug > 1)
-                       printk(" e%d=%08x",btv->gq_grab,btv->risc_cap_odd);
-               flags |= 3;
-               btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd);
-       } else if ((flags&2) &&
-                  (!btv->win.interlace || 0 == btv->risc_cap_even)) {
-               if (bttv_debug > 1)
-                       printk(" eo=%08lx",virt_to_bus(btv->risc_scr_odd));
-               btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_scr_odd));
-       } else {
-               if (bttv_debug > 1)
-                       printk(" -----------");
-               btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_jmp+6));
-       }
+       name:     "bt848/878 radio",
+       type:     VID_TYPE_TUNER|VID_TYPE_TELETEXT,
+       hardware: VID_HARDWARE_BT848,
+       fops:     &radio_fops,
+       kernel_ioctl: radio_ioctl,
+       minor:    -1,
+};
 
+/* ----------------------------------------------------------------------- */
+/* irq handler                                                             */
 
-       /* Sync to start of even field */
-       btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC
-                                |BT848_FIFO_STATUS_VRO);
-       btv->risc_jmp[7]=cpu_to_le32(0);
+static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
+                           "VPRES", "6", "7", "I2CDONE", "GPINT", "10",
+                           "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
+                           "RIPERR", "PABORT", "OCERR", "SCERR" };
 
-       /* Jump to even vbi sub */
-       btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP);
-       if (flags&4) {
-               if (bttv_debug > 1)
-                       printk(" ov=%08lx",virt_to_bus(btv->vbi_even));
-               btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even));
-       } else {
-               if (bttv_debug > 1)
-                       printk(" -----------");
-               btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
-       }
-
-       /* Jump to even sub */
-       btv->risc_jmp[10]=cpu_to_le32(BT848_RISC_JUMP|(8<<20));
-       if (0 != btv->risc_cap_even) {
-               if (bttv_debug > 1)
-                       printk(" o%d=%08x",btv->gq_grab,btv->risc_cap_even);
-               flags |= 3;
-               btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even);
-       } else if ((flags&1) &&
-                  btv->win.interlace) {
-               if (bttv_debug > 1)
-                       printk(" oo=%08lx",virt_to_bus(btv->risc_scr_even));
-               btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_scr_even));
-       } else {
-               if (bttv_debug > 1)
-                       printk(" -----------");
-               btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12));
+static void bttv_print_irqbits(u32 print, u32 mark)
+{
+       int i;
+       
+       printk("bits:");
+       for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
+               if (print & (1 << i))
+                       printk(" %s",irq_name[i]);
+               if (mark & (1 << i))
+                       printk("*");
        }
-
-       if (btv->gq_start) {
-               btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
-       } else {
-               btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP);
-       }
-       btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp));
-
-       /* enable cpaturing and DMA */
-       if (bttv_debug > 1)
-               printk(" flags=0x%x dma=%s\n",
-                      flags,(flags&0x0f) ? "on" : "off");
-       btaor(flags, ~0x0f, BT848_CAP_CTL);
-       if (flags&0x0f)
-               bt848_dma(btv, 3);
-       else
-               bt848_dma(btv, 0);
 }
 
-# define do_video_register(dev,type,nr) video_register_device(dev,type,nr)
-
-static int __devinit init_video_dev(struct bttv *btv)
+static void bttv_print_riscaddr(struct bttv *btv)
 {
-       audio(btv, AUDIO_MUTE, 1);
-        
-       if(do_video_register(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
-               return -1;
-       if(do_video_register(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) 
-        {
-               video_unregister_device(&btv->video_dev);
-               return -1;
-       }
-       if (btv->has_radio)
-       {
-               if(do_video_register(&btv->radio_dev, VFL_TYPE_RADIO, radio_nr)<0) 
-                {
-                       video_unregister_device(&btv->vbi_dev);
-                       video_unregister_device(&btv->video_dev);
-                       return -1;
-               }
-       }
-        return 1;
+       printk("  main: %08Lx\n",
+              (u64)btv->main.dma);
+       printk("  vbi : o=%08Lx e=%08Lx\n",
+              btv->vcurr ? (u64)btv->vcurr->odd.dma : 0,
+              btv->vcurr ? (u64)btv->vcurr->even.dma : 0);
+       printk("  cap : o=%08Lx e=%08Lx\n",
+              btv->odd ? (u64)btv->odd->odd.dma : 0,
+              btv->even ? (u64)btv->even->even.dma : 0);
+       printk("  scr : o=%08Lx e=%08Lx\n",
+              btv->screen ? (u64)btv->screen->odd.dma  : 0,
+              btv->screen ? (u64)btv->screen->even.dma : 0);
 }
 
-static int __devinit init_bt848(struct bttv *btv)
+static void bttv_irq_timeout(unsigned long data)
 {
-       int j;
-       unsigned long irq_flags;
-
-       btv->user=0; 
-        init_MUTEX(&btv->lock);
-
-       /* dump current state of the gpio registers before changing them,
-        * might help to make a new card work */
-       if (bttv_gpio)
-               bttv_gpio_tracking(btv,"init #1");
+       struct bttv *btv = (struct bttv *)data;
+       struct bttv_buffer *o_even,*o_odd,*o_vcurr;
+       struct bttv_buffer *capture;
 
-       /* reset the bt848 */
-       btwrite(0, BT848_SRESET);
-       DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n", btv->nr, (unsigned long) btv->bt848_mem));
-
-       /* not registered yet */
-       btv->video_dev.minor = -1;
-       btv->radio_dev.minor = -1;
-       btv->vbi_dev.minor = -1;
-
-       /* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */
-       btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */
-       btv->win.interlace=1;
-       btv->win.x=0;
-       btv->win.y=0;
-       btv->win.width=320;
-       btv->win.height=240;
-       btv->win.bpp=2;
-       btv->win.depth=16;
-       btv->win.color_fmt=BT848_COLOR_FMT_RGB16;
-       btv->win.bpl=1024*btv->win.bpp;
-       btv->win.swidth=1024;
-       btv->win.sheight=768;
-       btv->win.vidadr=0;
-       btv->vbi_on=0;
-       btv->scr_on=0;
-
-       btv->risc_scr_odd=0;
-       btv->risc_scr_even=0;
-       btv->risc_cap_odd=0;
-       btv->risc_cap_even=0;
-       btv->risc_jmp=0;
-       btv->vbibuf=0;
-        btv->field=btv->last_field=0;
+       if (bttv_verbose) {
+               printk(KERN_INFO "bttv%d: timeout: risc=%08x, ",
+                      btv->nr,btread(BT848_RISC_COUNT));
+               bttv_print_irqbits(btread(BT848_INT_STAT),0);
+               printk("\n");
+       }
 
-       btv->errors=0;
-       btv->needs_restart=0;
-       btv->has_radio=radio[btv->nr];
+       spin_lock(&btv->s_lock);
+       o_odd   = btv->odd;
+       o_even  = btv->even;
+       o_vcurr = btv->vcurr;
+       btv->odd   = NULL;
+       btv->even  = NULL;
+       btv->vcurr = NULL;
+       
+       /* deactivate stuff */
+       bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+       bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+       bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0);
+       bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0);
+       bttv_set_dma(btv, 0, 0);
+
+       /* wake up + free */
+       if (o_odd == o_even) {
+               if (NULL != o_odd) {
+                       o_odd->vb.state = STATE_ERROR;
+                       wake_up(&o_odd->vb.done);
+               }
+       } else {
+               if (NULL != o_odd) {
+                       o_odd->vb.state = STATE_ERROR;
+                       wake_up(&o_odd->vb.done);
+               }
+               if (NULL != o_even) {
+                       o_even->vb.state = STATE_ERROR;
+                       wake_up(&o_even->vb.done);
+               }
+       }
+       if (NULL != o_vcurr) {
+               o_vcurr->vb.state = STATE_ERROR;
+               wake_up(&o_vcurr->vb.done);
+       }
 
-       if (!(btv->risc_scr_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
-               return -1;
-       if (!(btv->risc_scr_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
-               return -1;
-       if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL)))
-               return -1;
-       btv->vbi_odd=btv->risc_jmp+16;
-       btv->vbi_even=btv->vbi_odd+256;
-       btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12);
-       btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6);
-
-       btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD);
-       btv->vbibuf=(unsigned char *) vmalloc_32(VBIBUF_SIZE);
-       if (!btv->vbibuf) 
-               return -1;
-       if (!(btv->gbuf = kmalloc(sizeof(struct bttv_gbuf)*gbuffers,GFP_KERNEL)))
-               return -1;
-       for (j = 0; j < gbuffers; j++) {
-               if (!(btv->gbuf[j].risc = kmalloc(16384,GFP_KERNEL)))
-                       return -1;
+       /* cancel all outstanding capture / vbi requests */
+       while (!list_empty(&btv->capture)) {
+               capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+               list_del(&capture->vb.queue);
+               capture->vb.state = STATE_ERROR;
+               wake_up(&capture->vb.done);
+       }
+       while (!list_empty(&btv->vcapture)) {
+               capture = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+               list_del(&capture->vb.queue);
+               capture->vb.state = STATE_ERROR;
+               wake_up(&capture->vb.done);
        }
        
-       memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random
-                                               memory to the user */
-
-       btv->fbuffer=NULL;
-
-/*     btwrite(0, BT848_TDEC); */
-        btwrite(0x10, BT848_COLOR_CTL);
-       btwrite(0x00, BT848_CAP_CTL);
-       /* set planar and packed mode trigger points and         */
-       /* set rising edge of inverted GPINTR pin as irq trigger */
-       btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
-               BT848_GPIO_DMA_CTL_PLTP1_16|
-               BT848_GPIO_DMA_CTL_PLTP23_16|
-               BT848_GPIO_DMA_CTL_GPINTC|
-               BT848_GPIO_DMA_CTL_GPINTI, 
-               BT848_GPIO_DMA_CTL);
-
-        /* select direct input */
-       btwrite(0x00, BT848_GPIO_REG_INP);
-       btwrite(0x00, BT848_GPIO_OUT_EN);
-       if (bttv_gpio)
-               bttv_gpio_tracking(btv,"init #2");
+       btv->errors++;
+       spin_unlock(&btv->s_lock);      
+}
 
-       btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO,
-               BT848_IFORM);
+static void
+bttv_irq_switch_fields(struct bttv *btv)
+{
+       struct bttv_buffer *o_even,*o_odd,*o_vcurr;
+       struct bttv_buffer *capture;
+       int irqflags = 0;
+#ifdef HAVE_V4L2
+       stamp_t ts;
+#endif
 
-       btwrite(0xd8, BT848_CONTRAST_LO);
-       bt848_bright(btv, 0x10);
+       spin_lock(&btv->s_lock);
+       o_odd   = btv->odd;
+       o_even  = btv->even;
+       o_vcurr = btv->vcurr;
+       btv->odd   = NULL;
+       btv->even  = NULL;
+       btv->vcurr = NULL;
+
+       /* vbi request ? */
+       if (!list_empty(&btv->vcapture)) {
+               irqflags = 1;
+               btv->vcurr = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue);
+               list_del(&btv->vcurr->vb.queue);
+       }
+
+       /* capture request ? */
+       if (!list_empty(&btv->capture)) {
+               irqflags = 1;
+               capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+               list_del(&capture->vb.queue);
+               if (capture->vb.field & VBUF_FIELD_ODD)
+                       btv->odd = capture;
+               if (capture->vb.field & VBUF_FIELD_EVEN)
+                       btv->even = capture;
+
+               /* capture request for other field ? */
+               if (!(capture->vb.field & VBUF_FIELD_INTER) &&
+                   !list_empty(&btv->capture)) {
+                       capture = list_entry(btv->capture.next, struct bttv_buffer, vb.queue);
+                       if (!(capture->vb.field & VBUF_FIELD_INTER)) {
+                               if (NULL == btv->odd &&
+                                   capture->vb.field & VBUF_FIELD_ODD) {
+                                       btv->odd = capture;
+                                       list_del(&capture->vb.queue);
+                               }
+                               if (NULL == btv->even &&
+                                   capture->vb.field & VBUF_FIELD_EVEN) {
+                                       btv->even = capture;
+                                       list_del(&capture->vb.queue);
+                               }
+                       }
+               }
+       }
 
-       btwrite(0x20, BT848_E_VSCALE_HI);
-       btwrite(0x20, BT848_O_VSCALE_HI);
-       btwrite(/*BT848_ADC_SYNC_T|*/
-               BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC);
+       /* screen overlay ? */
+       if (NULL != btv->screen) {
+               if (btv->screen->vb.field & VBUF_FIELD_INTER) {
+                       if (NULL == btv->odd && NULL == btv->even) {
+                               btv->odd  = btv->screen;
+                               btv->even = btv->screen;
+                       }
+               } else {
+                       if ((btv->screen->vb.field & VBUF_FIELD_ODD) &&
+                           NULL == btv->odd) {
+                               btv->odd = btv->screen;
+                       }
+                       if ((btv->screen->vb.field & VBUF_FIELD_EVEN) &&
+                           NULL == btv->even) {
+                               btv->even = btv->screen;
+                       }
+               }
+       }
 
-       if (lumafilter) {
-               btwrite(0, BT848_E_CONTROL);
-               btwrite(0, BT848_O_CONTROL);
+       if (irq_debug)
+               printk(KERN_DEBUG
+                      "bttv: irq odd=%p even=%p screen=%p vbi=%p\n",
+                      btv->odd,btv->even,btv->screen,btv->vcurr);
+       
+       /* activate new fields */
+       bttv_buffer_activate(btv,btv->odd,btv->even);
+       if (btv->vcurr) {
+               btv->vcurr->vb.state = STATE_ACTIVE;
+               bttv_risc_hook(btv, RISC_SLOT_O_VBI, &btv->vcurr->odd, 0);
+               bttv_risc_hook(btv, RISC_SLOT_E_VBI, &btv->vcurr->even, 0);
        } else {
-               btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
-               btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
+               bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0);
+               bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0);
        }
-
-       btv->picture.colour=254<<7;
-       btv->picture.brightness=128<<8;
-       btv->picture.hue=128<<8;
-       btv->picture.contrast=0xd8<<7;
-
-       btwrite(0x00, BT848_E_SCLOOP);
-       btwrite(0x00, BT848_O_SCLOOP);
-
-       /* clear interrupt status */
-       btwrite(0xfffffUL, BT848_INT_STAT);
-        
-       /* set interrupt mask */
-       btwrite(btv->triton1|
-                /*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|
-                  BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/
-                (fieldnr ? BT848_INT_VSYNC : 0)|
-               BT848_INT_GPINT|
-               BT848_INT_SCERR|
-               BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
-               BT848_INT_FMTCHG|BT848_INT_HLOCK,
-               BT848_INT_MASK);
-
-       bt848_muxsel(btv, 1);
-       bt848_set_winsize(btv);
-       make_vbitab(btv);
-       spin_lock_irqsave(&btv->s_lock, irq_flags);
-       bt848_set_risc_jmps(btv,-1);
-       spin_unlock_irqrestore(&btv->s_lock, irq_flags);
-
-       /* needs to be done before i2c is registered */
-        if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
-                bttv_boot_msp34xx(btv,5);
-       if (btv->type == BTTV_VOODOOTV_FM)
-               bttv_boot_msp34xx(btv,20);
-
-       /* register i2c */
-        btv->tuner_type=-1;
-        init_bttv_i2c(btv);
-
-       /* some card-specific stuff (needs working i2c) */
-       bttv_init_card(btv);
-
-       /*
-        *      Now add the template and register the device unit.
-        */
-        init_video_dev(btv);
-
-       return 0;
+       bttv_set_dma(btv, 0, irqflags);
+       
+       /* wake up + free */
+#ifdef HAVE_V4L2
+       v4l2_masterclock_gettime(&ts);
+#endif
+       if (o_odd == o_even) {
+               if (NULL != o_odd && btv->odd != o_odd) {
+#ifdef HAVE_V4L2
+                       o_odd->vb.ts = ts;
+#endif
+                       o_odd->vb.field_count = btv->field_count;
+                       o_odd->vb.state = STATE_DONE;
+                       wake_up(&o_odd->vb.done);
+               }
+       } else {
+               if (NULL != o_odd && btv->odd != o_odd) {
+#ifdef HAVE_V4L2
+                       o_odd->vb.ts = ts;
+#endif
+                       o_odd->vb.field_count = btv->field_count;
+                       o_odd->vb.state = STATE_DONE;
+                       wake_up(&o_odd->vb.done);
+               }
+               if (NULL != o_even && btv->even != o_even) {
+#ifdef HAVE_V4L2
+                       o_even->vb.ts = ts;
+#endif
+                       o_even->vb.field_count = btv->field_count;
+                       o_even->vb.state = STATE_DONE;
+                       wake_up(&o_even->vb.done);
+               }
+       }
+       if (NULL != o_vcurr) {
+#ifdef HAVE_V4L2
+               o_vcurr->vb.ts = ts;
+#endif
+               o_vcurr->vb.field_count = btv->field_count;
+               o_vcurr->vb.state = STATE_DONE;
+               wake_up(&o_vcurr->vb.done);
+       }
+       spin_unlock(&btv->s_lock);
 }
 
-/* ----------------------------------------------------------------------- */
-
-static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
-                           "VPRES", "6", "7", "I2CDONE", "GPINT", "10",
-                           "RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
-                           "RIPERR", "PABORT", "OCERR", "SCERR" };
-
 static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
 {
        u32 stat,astat;
@@ -2660,15 +3039,11 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
                dstat=btread(BT848_DSTATUS);
 
                if (irq_debug) {
-                       int i;
-                       printk(KERN_DEBUG "bttv%d: irq loop=%d risc=%x, bits:",
-                              btv->nr, count, stat>>28);
-                       for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
-                               if (stat & (1 << i))
-                                       printk(" %s",irq_name[i]);
-                               if (astat & (1 << i))
-                                       printk("*");
-                       }
+                       printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d "
+                              "riscs=%x, riscc=%08x, ",
+                              btv->nr, count, btv->field_count,
+                              stat>>28, btread(BT848_RISC_COUNT));
+                       bttv_print_irqbits(stat,astat);
                        if (stat & BT848_INT_HLOCK)
                                printk("   HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
                                       ? "yes" : "no");
@@ -2681,311 +3056,316 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
                        printk("\n");
                }
 
-               if (astat&BT848_INT_GPINT)
-                       wake_up_interruptible(&btv->gpioq);
-
                if (astat&BT848_INT_VSYNC) 
-                        btv->field++;
-
-               if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) {
-                       if (bttv_verbose)
-                               printk("bttv%d: irq:%s%s risc_count=%08x\n",
-                                      btv->nr,
-                                      (astat&BT848_INT_SCERR) ? " SCERR" : "",
-                                      (astat&BT848_INT_OCERR) ? " OCERR" : "",
-                                      btread(BT848_RISC_COUNT));
-                       btv->errors++;
-                       if (btv->errors < BTTV_ERRORS) {
-                               spin_lock(&btv->s_lock);
-                               btand(~15, BT848_GPIO_DMA_CTL);
-                               btwrite(virt_to_bus(btv->risc_jmp+2),
-                                       BT848_RISC_STRT_ADD);
-                               bt848_set_geo(btv,0);
-                               bt848_set_risc_jmps(btv,-1);
-                               spin_unlock(&btv->s_lock);
-                       } else {
-                               if (bttv_verbose)
-                                       printk("bttv%d: aiee: error loops\n",btv->nr);
-                               bt848_offline(btv);
-                       }
-               }
-               if (astat&BT848_INT_RISCI) 
-               {
-                       if (bttv_debug > 1)
-                               printk("bttv%d: IRQ_RISCI\n",btv->nr);
-
-                       /* captured VBI frame */
-                       if (stat&(1<<28)) 
-                       {
-                               btv->vbip=0;
-                               /* inc vbi frame count for detecting drops */
-                               (*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++;
-                               wake_up_interruptible(&btv->vbiq);
-                       }
+                        btv->field_count++;
 
-                       /* captured full frame */
-                       if (stat&(2<<28) && btv->gq_grab != -1) 
-                       {
-                                btv->last_field=btv->field;
-                               if (bttv_debug)
-                                       printk("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab);
-                               do_gettimeofday(&btv->gbuf[btv->gq_grab].tv);
-                               spin_lock(&btv->s_lock);
-                               btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE;
-                               btv->gq_grab = -1;
-                               if (btv->gq_in != btv->gq_out)
-                               {
-                                       btv->gq_grab = btv->gqueue[btv->gq_out++];
-                                       btv->gq_out  = btv->gq_out % MAX_GBUFFERS;
-                                       if (bttv_debug)
-                                               printk("bttv%d: cap irq: capture %d\n",btv->nr,btv->gq_grab);
-                                        btv->risc_cap_odd  = btv->gbuf[btv->gq_grab].ro;
-                                       btv->risc_cap_even = btv->gbuf[btv->gq_grab].re;
-                                       bt848_set_risc_jmps(btv,-1);
-                                       bt848_set_geo(btv,0);
-                                       btwrite(BT848_COLOR_CTL_GAMMA,
-                                               BT848_COLOR_CTL);
-                               } else {
-                                        btv->risc_cap_odd  = 0;
-                                       btv->risc_cap_even = 0;
-                                       bt848_set_risc_jmps(btv,-1);
-                                        bt848_set_geo(btv,0);
-                                       btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA,
-                                               BT848_COLOR_CTL);
-                               }
-                               spin_unlock(&btv->s_lock);
-                               wake_up_interruptible(&btv->capq);
-                               break;
-                       }
-                       if (stat&(8<<28) && btv->gq_start) 
-                       {
-                               spin_lock(&btv->s_lock);
-                               btv->gq_start = 0;
-                               btv->gq_grab = btv->gqueue[btv->gq_out++];
-                               btv->gq_out  = btv->gq_out % MAX_GBUFFERS;
-                               if (bttv_debug)
-                                       printk("bttv%d: cap irq: capture %d [start]\n",btv->nr,btv->gq_grab);
-                               btv->risc_cap_odd  = btv->gbuf[btv->gq_grab].ro;
-                               btv->risc_cap_even = btv->gbuf[btv->gq_grab].re;
-                               bt848_set_risc_jmps(btv,-1);
-                               bt848_set_geo(btv,0);
-                               btwrite(BT848_COLOR_CTL_GAMMA,
-                                       BT848_COLOR_CTL);
-                               spin_unlock(&btv->s_lock);
-                       }
-               }
+               if (astat & BT848_INT_GPINT)
+                       wake_up(&btv->gpioq);
+               
+                if ((astat & BT848_INT_RISCI)  &&  (stat & (1<<28)))
+                       bttv_irq_switch_fields(btv);
 
-               if (astat&BT848_INT_HLOCK) {
-                       if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
-                               audio(btv, AUDIO_ON,0);
+               if ((astat & BT848_INT_HLOCK)  &&  btv->opt_automute) {
+                       if ((dstat & BT848_DSTATUS_HLOC) || btv->radio_user)
+                               audio_mux(btv, AUDIO_UNMUTE);
                        else
-                               audio(btv, AUDIO_OFF,0);
+                               audio_mux(btv, AUDIO_MUTE);
                }
-    
+
+               if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
+                       printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->nr,
+                              (astat & BT848_INT_SCERR) ? "SCERR" : "",
+                              (astat & BT848_INT_OCERR) ? "OCERR" : "",
+                              btread(BT848_RISC_COUNT));
+                       bttv_print_irqbits(stat,astat);
+                       printk("\n");
+                       if (bttv_debug)
+                               bttv_print_riscaddr(btv);
+               }
+               if (fdsr && astat & BT848_INT_FDSR) {
+                       printk(KERN_INFO "bttv%d: FDSR @ %08x\n",
+                              btv->nr,btread(BT848_RISC_COUNT));
+                       if (bttv_debug)
+                               bttv_print_riscaddr(btv);
+               }
+
                count++;
                if (count > 20) {
                        btwrite(0, BT848_INT_MASK);
                        printk(KERN_ERR 
                               "bttv%d: IRQ lockup, cleared int mask\n", btv->nr);
-                       bt848_offline(btv);
                }
        }
 }
 
 
+/* ----------------------------------------------------------------------- */
+/* initialitation                                                          */
 
-/*
- *     Scan for a Bt848 card, request the irq and map the io memory 
- */
-
-/* Can't be marked __devexit with a reference from bttv_probe.  */
-static void bttv_remove(struct pci_dev *pci_dev)
+/* register video4linux devices */
+static int __devinit bttv_register_video(struct bttv *btv)
 {
-        u8 command;
-        int j;
-        struct bttv *btv = pci_get_drvdata(pci_dev);
-
-       if (bttv_verbose)
-               printk("bttv%d: unloading\n",btv->nr);
-
-        /* unregister i2c_bus */
-       if (0 == btv->i2c_rc)
-               i2c_bit_del_bus(&btv->i2c_adap);
-
-        /* turn off all capturing, DMA and IRQs */
-        btand(~15, BT848_GPIO_DMA_CTL);
-
-        /* first disable interrupts before unmapping the memory! */
-        btwrite(0, BT848_INT_MASK);
-        btwrite(~0x0UL,BT848_INT_STAT);
-        btwrite(0x0, BT848_GPIO_OUT_EN);
-       if (bttv_gpio)
-               bttv_gpio_tracking(btv,"cleanup");
-
-        /* disable PCI bus-mastering */
-        pci_read_config_byte(btv->dev, PCI_COMMAND, &command);
-        command &= ~PCI_COMMAND_MASTER;
-        pci_write_config_byte(btv->dev, PCI_COMMAND, command);
-
-        /* unmap and free memory */
-        for (j = 0; j < gbuffers; j++)
-                if (btv->gbuf[j].risc)
-                        kfree(btv->gbuf[j].risc);
-        if (btv->gbuf)
-                kfree((void *) btv->gbuf);
-
-        if (btv->risc_scr_odd)
-                kfree((void *) btv->risc_scr_odd);
-
-        if (btv->risc_scr_even)
-                kfree((void *) btv->risc_scr_even);
-
-        DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp));
-        if (btv->risc_jmp)
-                kfree((void *) btv->risc_jmp);
-
-        DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf));
-        if (btv->vbibuf)
-                vfree((void *) btv->vbibuf);
-
-        free_irq(btv->irq,btv);
-        DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem));
-        if (btv->bt848_mem)
-                iounmap(btv->bt848_mem);
+        if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
+                return -1;
+       printk(KERN_INFO "bttv%d: registered device video%d\n",
+              btv->nr,btv->video_dev.minor & 0x1f);
 
-        if (btv->video_dev.minor!=-1)
+        if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) {
                 video_unregister_device(&btv->video_dev);
-        if (btv->vbi_dev.minor!=-1)
-                video_unregister_device(&btv->vbi_dev);
-        if (btv->radio_dev.minor != -1)
-                video_unregister_device(&btv->radio_dev);
-
-        release_mem_region(pci_resource_start(btv->dev,0),
-                           pci_resource_len(btv->dev,0));
-        /* wake up any waiting processes
-           because shutdown flag is set, no new processes (in this queue)
-           are expected
-        */
-        btv->shutdown=1;
-        wake_up(&btv->gpioq);
+                return -1;
+        }
+       printk(KERN_INFO "bttv%d: registered device vbi%d\n",
+              btv->nr,btv->vbi_dev.minor & 0x1f);
 
-       pci_set_drvdata(pci_dev, NULL);
-        return;
+        if (!btv->has_radio)
+               return 0;
+       if (video_register_device(&btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) {
+               video_unregister_device(&btv->vbi_dev);
+               video_unregister_device(&btv->video_dev);
+               return -1;
+        }
+       printk(KERN_INFO "bttv%d: registered device radio%d\n",
+              btv->nr,btv->radio_dev.minor & 0x1f);
+        return 0;
 }
 
 
-static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
+/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
+/* response on cards with no firmware is not enabled by OF */
+static void pci_set_command(struct pci_dev *dev)
 {
-       int result;
-       unsigned char lat;
-       struct bttv *btv;
 #if defined(__powerpc__)
         unsigned int cmd;
+       
+        pci_read_config_dword(dev, PCI_COMMAND, &cmd);
+        cmd = (cmd | PCI_COMMAND_MEMORY ); 
+        pci_write_config_dword(dev, PCI_COMMAND, cmd);
 #endif
+}
 
-       printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
+static int __devinit bttv_probe(struct pci_dev *dev,
+                               const struct pci_device_id *pci_id)
+{
+       int result;
+       unsigned char lat;
+       struct bttv *btv;
 
+       if (bttv_num == BTTV_MAX)
+               return -ENOMEM;
+       printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
         btv=&bttvs[bttv_num];
-        btv->dev=dev;
-        btv->nr = bttv_num;
-        btv->bt848_mem=NULL;
-        btv->vbibuf=NULL;
-        btv->risc_jmp=NULL;
-        btv->vbi_odd=NULL;
-        btv->vbi_even=NULL;
-        init_waitqueue_head(&btv->vbiq);
-        init_waitqueue_head(&btv->capq);
-        btv->vbip=VBIBUF_SIZE;
-       btv->s_lock = SPIN_LOCK_UNLOCKED;
-       init_waitqueue_head(&btv->gpioq);
-       btv->shutdown=0;
+       memset(btv,0,sizeof(*btv));
+       btv->nr  = bttv_num;
+       sprintf(btv->name,"bttv%d",btv->nr);
+
+       /* initialize structs / fill in defaults */
+        init_MUTEX(&btv->lock);
+        init_MUTEX(&btv->reslock);
+        btv->s_lock = SPIN_LOCK_UNLOCKED;
+        init_waitqueue_head(&btv->gpioq);
+        INIT_LIST_HEAD(&btv->capture);
+        INIT_LIST_HEAD(&btv->vcapture);
+
+        init_MUTEX(&btv->vbi.lock);
+       INIT_LIST_HEAD(&btv->vbi.stream);
+
+       btv->timeout.function = bttv_irq_timeout;
+       btv->timeout.data     = (unsigned long)btv;
        
-       memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
-       memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
-       memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
+        btv->i2c_rc = -1;
+        btv->tuner_type = -1;
+
+       memcpy(&btv->video_dev, &bttv_template,     sizeof(bttv_template));
+       memcpy(&btv->radio_dev, &radio_template,    sizeof(radio_template));
+       memcpy(&btv->vbi_dev,   &bttv_vbi_template, sizeof(bttv_vbi_template));
+        btv->video_dev.minor = -1;
+       btv->video_dev.priv = btv;
+        btv->radio_dev.minor = -1;
+       btv->radio_dev.priv = btv;
+        btv->vbi_dev.minor = -1;
+       btv->vbi_dev.priv = btv;
+       btv->has_radio=radio[btv->nr];
        
-        btv->id=dev->device;
-        btv->irq=dev->irq;
-       btv->bt848_adr=pci_resource_start(dev,0);
-       if (pci_enable_device(dev))
+       /* pci stuff (init, get irq/mmip, ... */
+       btv->dev = dev;
+        btv->id  = dev->device;
+       if (pci_enable_device(dev)) {
+                printk(KERN_WARNING "bttv%d: Can't enable device.\n",
+                      btv->nr);
                return -EIO;
+       }
+        if (pci_set_dma_mask(dev, 0xffffffff)) {
+                printk(KERN_WARNING "bttv%d: No suitable DMA available.\n",
+                      btv->nr);
+               return -EIO;
+        }
        if (!request_mem_region(pci_resource_start(dev,0),
                                pci_resource_len(dev,0),
-                               "bttv")) {
+                               btv->name)) {
                return -EBUSY;
        }
-        if (btv->id >= 878)
-                btv->i2c_command = 0x83;                   
-        else
-                btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
+        pci_set_master(dev);
+       pci_set_command(dev);
+       pci_set_drvdata(dev,btv);
+       if (!pci_dma_supported(dev,0xffffffff)) {
+               printk("bttv: Oops: no 32bit PCI DMA ???\n");
+               result = -EIO;
+               goto fail1;
+       }
 
+       if (-1 != latency) {
+               printk(KERN_INFO "bttv%d: setting pci latency timer to %d\n",
+                      bttv_num,latency);
+               pci_write_config_byte(dev, PCI_LATENCY_TIMER, latency);
+       }
         pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
         pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
         printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %02x:%02x.%x, ",
                bttv_num,btv->id, btv->revision, dev->bus->number,
               PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
-        printk("irq: %d, latency: %d, memory: 0x%lx\n",
-              btv->irq, lat, btv->bt848_adr);
+        printk("irq: %d, latency: %d, mmio: 0x%lx\n",
+              btv->dev->irq, lat, pci_resource_start(dev,0));
        
-       bttv_idcard(btv);
-
-#if defined(__powerpc__)
-        /* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
-        /* response on cards with no firmware is not enabled by OF */
-        pci_read_config_dword(dev, PCI_COMMAND, &cmd);
-        cmd = (cmd | PCI_COMMAND_MEMORY ); 
-        pci_write_config_dword(dev, PCI_COMMAND, cmd);
-#endif
-
 #ifdef __sparc__
-       btv->bt848_mem=(unsigned char *)btv->bt848_adr;
+       /* why no ioremap for sparc? */
+       btv->bt848_mmio=(unsigned char *)pci_resource_start(dev,0);
 #else
-       btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
+       btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000);
 #endif
-        
-        /* clear interrupt mask */
-       btwrite(0, BT848_INT_MASK);
 
-        result = request_irq(btv->irq, bttv_irq,
-                             SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
-        if (result==-EINVAL) 
-        {
-                printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
-                       bttv_num);
-               goto fail1;
-        }
-        if (result==-EBUSY)
-        {
-                printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
+        /* identify card */
+       bttv_idcard(btv);
+
+        /* disable irqs, register irq handler */
+       btwrite(0, BT848_INT_MASK);
+        result = request_irq(btv->dev->irq, bttv_irq,
+                             SA_SHIRQ | SA_INTERRUPT,btv->name,(void *)btv);
+        if (result < 0) {
+                printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
+                      bttv_num,btv->dev->irq);
                goto fail1;
         }
-        if (result < 0) 
-               goto fail1;
-        
+
        if (0 != bttv_handle_chipset(btv)) {
-               result = -1;
+               result = -EIO;
                goto fail2;
-       }
+        }
+
+       /* init options from insmod args */
+       btv->opt_combfilter = combfilter;
+       btv->opt_lumafilter = lumafilter;
+       btv->opt_automute   = automute;
+       btv->opt_chroma_agc = chroma_agc;
+       btv->opt_adc_crush  = adc_crush;
        
-        pci_set_master(dev);
-       pci_set_drvdata(dev,btv);
+       /* fill struct bttv with some useful defaults */
+       btv->init.btv = btv;
+       btv->init.ov.width      = 320;
+       btv->init.ov.height     = 240;
+       btv->init.buf.fmt       = format_by_palette(VIDEO_PALETTE_RGB24);
+       btv->init.buf.vb.width  = 320;
+       btv->init.buf.vb.height = 240;
+       btv->input = 0;
+
+       /* initialize hardware */
+        if (bttv_gpio)
+                bttv_gpio_tracking(btv,"pre-init");
+
+       bttv_risc_init_main(btv);
+       init_bt848(btv);
+
+       /* gpio */
+        btwrite(0x00, BT848_GPIO_REG_INP);
+        btwrite(0x00, BT848_GPIO_OUT_EN);
+        if (bttv_gpio)
+                bttv_gpio_tracking(btv,"init");
+
+        /* interrupt */
+        btwrite(0xfffffUL, BT848_INT_STAT);
+        btwrite((btv->triton1) |
+                BT848_INT_GPINT|
+                BT848_INT_SCERR|
+                (fdsr ? BT848_INT_FDSR : 0) |
+                BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
+                BT848_INT_FMTCHG|BT848_INT_HLOCK,
+                BT848_INT_MASK);
+       
+        /* needs to be done before i2c is registered */
+        bttv_init_card1(btv);
 
-       if(init_bt848(btv) < 0) {
-               bttv_remove(dev);
-               return -EIO;
-       }
-       bttv_num++;
+        /* register i2c */
+        init_bttv_i2c(btv);
+
+        /* some card-specific stuff (needs working i2c) */
+        bttv_init_card2(btv);
+
+        /* register video4linux */
+        bttv_register_video(btv);
 
+       bt848_bright(btv,32768);
+       bt848_contrast(btv,32768);
+       bt848_hue(btv,32768);
+       bt848_sat(btv,32768);
+       audio_mux(btv,AUDIO_MUTE);
+       set_input(btv,0);
+
+       /* everything is fine */
+       bttv_num++;
         return 0;
 
  fail2:
-        free_irq(btv->irq,btv);
+        free_irq(btv->dev->irq,btv);
+       
  fail1:
        release_mem_region(pci_resource_start(btv->dev,0),
                           pci_resource_len(btv->dev,0));
+       pci_set_drvdata(dev,NULL);
        return result;
 }
 
+static void __devexit bttv_remove(struct pci_dev *pci_dev)
+{
+        struct bttv *btv = pci_get_drvdata(pci_dev);
+
+       if (bttv_verbose)
+               printk("bttv%d: unloading\n",btv->nr);
+
+        /* shutdown everything (DMA+IRQs) */
+       btand(~15, BT848_GPIO_DMA_CTL);
+       btwrite(0, BT848_INT_MASK);
+       btwrite(~0x0UL,BT848_INT_STAT);
+       btwrite(0x0, BT848_GPIO_OUT_EN);
+       if (bttv_gpio)
+               bttv_gpio_tracking(btv,"cleanup");
+
+       /* tell gpio modules we are leaving ... */
+       btv->shutdown=1;
+       wake_up(&btv->gpioq);
+
+        /* unregister i2c_bus */
+       if (0 == btv->i2c_rc)
+               i2c_bit_del_bus(&btv->i2c_adap);
+
+       /* unregister video4linux */
+        if (btv->video_dev.minor!=-1)
+                video_unregister_device(&btv->video_dev);
+        if (btv->radio_dev.minor!=-1)
+                video_unregister_device(&btv->radio_dev);
+       if (btv->vbi_dev.minor!=-1)
+                video_unregister_device(&btv->vbi_dev);
+
+       /* free allocated memory */
+       bttv_riscmem_free(btv->dev,&btv->main);
+
+       /* free ressources */
+        free_irq(btv->dev->irq,btv);
+        release_mem_region(pci_resource_start(btv->dev,0),
+                           pci_resource_len(btv->dev,0));
+
+       pci_set_drvdata(pci_dev, NULL);
+        return;
+}
+
 static struct pci_device_id bttv_pci_tbl[] __devinitdata = {
         {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
          PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
@@ -3007,28 +3387,30 @@ static struct pci_driver bttv_pci_driver = {
         remove:   bttv_remove,
 };
 
-int bttv_init_module(void)
+static int bttv_init_module(void)
 {
        bttv_num = 0;
 
-       printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n",
+       printk(KERN_INFO "bttv: driver version %d.%d.%d loaded "
+              BTTV_APIS "\n",
               (BTTV_VERSION_CODE >> 16) & 0xff,
               (BTTV_VERSION_CODE >> 8) & 0xff,
               BTTV_VERSION_CODE & 0xff);
-       if (gbuffers < 2 || gbuffers > MAX_GBUFFERS)
+       if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME)
                gbuffers = 2;
        if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF)
                gbufsize = BTTV_MAX_FBUF;
+       gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK;
        if (bttv_verbose)
-               printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n",
-                      gbuffers,gbufsize/1024,gbuffers*gbufsize/1024);
+               printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n",
+                      gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT);
 
        bttv_check_chipset();
 
        return pci_module_init(&bttv_pci_driver);
 }
 
-void bttv_cleanup_module(void)
+static void bttv_cleanup_module(void)
 {
        pci_unregister_driver(&bttv_pci_driver);
        return;
diff --git a/drivers/media/video/bttv-risc.c b/drivers/media/video/bttv-risc.c
new file mode 100644 (file)
index 0000000..d63b5b4
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+    bttv-risc.c  --  interfaces to other kernel modules
+
+    bttv risc code handling
+       - memory management
+       - generation
+
+    (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#define __NO_VERSION__ 1
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/iobuf.h>
+#include <linux/vmalloc.h>
+#include <linux/interrupt.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+
+#include "bttvp.h"
+
+/* ---------------------------------------------------------- */
+/* allocate/free risc memory                                  */
+
+int  bttv_riscmem_alloc(struct pci_dev *pci,
+                       struct bttv_riscmem *risc,
+                       unsigned int size)
+{
+       unsigned long  *cpu;
+       dma_addr_t     dma;
+       
+       cpu = pci_alloc_consistent(pci, size, &dma);
+       if (NULL == cpu)
+               return -ENOMEM;
+       memset(cpu,0,size);
+
+       if (risc->cpu && risc->size < size) {
+               /* realloc (enlarge buffer) -- copy old stuff */
+               memcpy(cpu,risc->cpu,risc->size);
+               bttv_riscmem_free(pci,risc);
+       }
+       risc->cpu  = cpu;
+       risc->dma  = dma;
+       risc->size = size;
+
+       return 0;
+}
+
+void bttv_riscmem_free(struct pci_dev *pci,
+                      struct bttv_riscmem *risc)
+{
+       if (NULL == risc->cpu)
+               return;
+       pci_free_consistent(pci, risc->size, risc->cpu, risc->dma);
+       memset(risc,0,sizeof(*risc));
+}
+
+/* ---------------------------------------------------------- */
+/* risc code generators                                       */
+
+int
+bttv_risc_packed(struct bttv *btv, struct bttv_riscmem *risc,
+                struct scatterlist *sglist,
+                int offset, int bpl, int padding, int lines)
+{
+       int instructions,rc,line,todo;
+       struct scatterlist *sg;
+       unsigned long *rp;
+
+       /* estimate risc mem: worst case is one write per page border +
+          one write per scan line + sync + jump (all 2 dwords) */
+       instructions  = (bpl * lines) / PAGE_SIZE + lines;
+       instructions += 2;
+       if ((rc = bttv_riscmem_alloc(btv->dev,risc,instructions*8)) < 0)
+               return rc;
+
+       /* sync instruction */
+       rp = risc->cpu;
+       *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+       *(rp++) = cpu_to_le32(0);
+
+       /* scan lines */
+       sg = sglist;
+       for (line = 0; line < lines; line++) {
+               while (offset >= sg_dma_len(sg)) {
+                       offset -= sg_dma_len(sg);
+                       sg++;
+               }
+               if (bpl <= sg_dma_len(sg)-offset) {
+                       /* fits into current chunk */
+                        *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+                                           BT848_RISC_EOL|bpl);
+                        *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+                        offset+=bpl;
+               } else {
+                       /* scanline needs to be splitted */
+                        todo = bpl;
+                        *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
+                                           (sg_dma_len(sg)-offset));
+                        *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
+                        todo -= (sg_dma_len(sg)-offset);
+                        offset = 0;
+                        sg++;
+                        while (todo > sg_dma_len(sg)) {
+                                *(rp++)=cpu_to_le32(BT848_RISC_WRITE|
+                                                   sg_dma_len(sg));
+                                *(rp++)=cpu_to_le32(sg_dma_address(sg));
+                               todo -= sg_dma_len(sg);
+                               sg++;
+                       }
+                        *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|
+                                           todo);
+                       *(rp++)=cpu_to_le32(sg_dma_address(sg));
+                       offset += todo;
+               }
+               offset += padding;
+       }
+
+       /* save pointer to jmp instruction address */
+       risc->jmp = rp;
+       return 0;
+}
+
+int
+bttv_risc_planar(struct bttv *btv, struct bttv_riscmem *risc,
+                struct scatterlist *sglist,
+                int yoffset, int ybpl, int ypadding, int ylines,
+                int uoffset, int voffset, int hshift, int vshift,
+                int cpadding)
+{
+       int instructions,rc,line,todo,ylen,chroma;
+       unsigned long *rp,ri;
+       struct scatterlist *ysg;
+       struct scatterlist *usg;
+       struct scatterlist *vsg;
+
+       /* estimate risc mem: worst case is one write per page border +
+          one write per scan line (5 dwords)
+          plus sync + jump (2 dwords) */
+       instructions  = (ybpl * ylines * 2) / PAGE_SIZE + ylines;
+       instructions += 2;
+       if ((rc = bttv_riscmem_alloc(btv->dev,risc,instructions*4*5)) < 0)
+               return rc;
+
+       /* sync instruction */
+       rp = risc->cpu;
+       *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
+       *(rp++) = cpu_to_le32(0);
+
+       /* scan lines */
+       ysg = sglist;
+       usg = sglist;
+       vsg = sglist;
+       for (line = 0; line < ylines; line++) {
+               switch (vshift) {
+               case 0:  chroma = 1;           break;
+               case 1:  chroma = !(line & 1); break;
+               case 2:  chroma = !(line & 3); break;
+               default: chroma = 0;
+               }
+               for (todo = ybpl; todo > 0; todo -= ylen) {
+                       /* go to next sg entry if needed */
+                       while (yoffset >= sg_dma_len(ysg)) {
+                               yoffset -= sg_dma_len(ysg);
+                               ysg++;
+                       }
+                       while (uoffset >= sg_dma_len(usg)) {
+                               uoffset -= sg_dma_len(usg);
+                               usg++;
+                       }
+                       while (voffset >= sg_dma_len(vsg)) {
+                               voffset -= sg_dma_len(vsg);
+                               vsg++;
+                       }
+
+                       /* calculate max number of bytes we can write */
+                       ylen = todo;
+                       if (yoffset + ylen > sg_dma_len(ysg))
+                               ylen = sg_dma_len(ysg) - yoffset;
+                       if (chroma) {
+                               if (uoffset + (ylen>>hshift) > sg_dma_len(usg))
+                                       ylen = (sg_dma_len(usg) - uoffset) << hshift;
+                               if (voffset + (ylen>>hshift) > sg_dma_len(vsg))
+                                       ylen = (sg_dma_len(vsg) - voffset) << hshift;
+                               ri = BT848_RISC_WRITE123;
+                       } else {
+                               ri = BT848_RISC_WRITE1S23;
+                       }
+                       if (ybpl == todo)
+                               ri |= BT848_RISC_SOL;
+                       if (ylen == todo)
+                               ri |= BT848_RISC_EOL;
+
+                       /* write risc instruction */
+                        *(rp++)=cpu_to_le32(ri | ylen);
+                        *(rp++)=cpu_to_le32(((ylen >> hshift) << 16) |
+                                           (ylen >> hshift));
+                       *(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset);
+                       yoffset += ylen;
+                       if (chroma) {
+                               *(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset);
+                               uoffset += ylen >> hshift;
+                               *(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset);
+                               voffset += ylen >> hshift;
+                       }
+               }
+               yoffset += ypadding;
+               if (chroma) {
+                       uoffset += cpadding;
+                       voffset += cpadding;
+               }
+       }
+
+       /* save pointer to jmp instruction address */
+       risc->jmp = rp;
+       return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+struct SKIPLIST {
+       int start;
+       int end;
+};
+
+int
+bttv_screen_clips(struct video_buffer *fbuf,
+                 int x, int y, int width, int height,
+                 struct video_clip *clips, int n)
+{
+       if (x < 0) {
+               /* left */
+               clips[n].x = 0;
+               clips[n].y = 0;
+               clips[n].width  = -x;
+               clips[n].height = height;
+               n++;
+       }
+       if (x+width > fbuf->width) {
+               /* right */
+               clips[n].x = fbuf->width - x;
+               clips[n].y = 0;
+               clips[n].width  = width - clips[n].x;
+               clips[n].height = height;
+               n++;
+       }
+       if (y < 0) {
+               /* top */
+               clips[n].x = 0;
+               clips[n].y = 0;
+               clips[n].width  = width;
+               clips[n].height = -y;
+               n++;
+       }
+       if (y+height > fbuf->height) {
+               /* bottom */
+               clips[n].x = 0;
+               clips[n].y = fbuf->height - y;
+               clips[n].width  = width;
+               clips[n].height = height - clips[n].y;
+               n++;
+       }
+       return n;
+}
+
+void
+bttv_sort_clips(struct video_clip *clips, int nclips)
+{
+       struct video_clip swap;
+       int i,j,n;
+
+       for (i = nclips-2; i >= 0; i--) {
+               for (n = 0, j = 0; j <= i; j++) {
+                       if (clips[j].x > clips[j+1].x) {
+                               swap = clips[j];
+                               clips[j] = clips[j+1];
+                               clips[j+1] = swap;
+                               n++;
+                       }
+               }
+               if (0 == n)
+                       break;
+       }
+}
+
+static void
+calc_skips(int line, int width, int *maxy,
+          struct SKIPLIST *skips, int *nskips,
+          const struct video_clip *clips, int nclips)
+{
+       int clip,skip,maxline,end;
+
+       skip=0;
+       maxline = 9999;
+       for (clip = 0; clip < nclips; clip++) {
+
+               /* sanity checks */
+               if (clips[clip].x + clips[clip].width <= 0)
+                       continue;
+               if (clips[clip].x > width)
+                       break;
+               
+               /* vertical range */
+               if (line > clips[clip].y+clips[clip].height-1)
+                       continue;
+               if (line < clips[clip].y) {
+                       if (maxline > clips[clip].y-1)
+                               maxline = clips[clip].y-1;
+                       continue;
+               }
+               if (maxline > clips[clip].y+clips[clip].height-1)
+                       maxline = clips[clip].y+clips[clip].height-1;
+
+               /* horizontal range */
+               if (0 == skip || clips[clip].x > skips[skip-1].end) {
+                       /* new one */
+                       skips[skip].start = clips[clip].x;
+                       if (skips[skip].start < 0)
+                               skips[skip].start = 0;
+                       skips[skip].end = clips[clip].x + clips[clip].width;
+                       if (skips[skip].end > width)
+                               skips[skip].end = width;
+                       skip++;
+               } else {
+                       /* overlaps -- expand last one */
+                       end = clips[clip].x + clips[clip].width;
+                       if (skips[skip-1].end < end)
+                               skips[skip-1].end = end;
+                       if (skips[skip-1].end > width)
+                               skips[skip-1].end = width;
+               }
+       }
+       *nskips = skip;
+       *maxy = maxline;
+
+       if (bttv_debug) {
+               printk(KERN_DEBUG "bttv: skips line %d-%d:",line,maxline);
+               for (skip = 0; skip < *nskips; skip++) {
+                       printk(" %d-%d",skips[skip].start,skips[skip].end);
+               }
+               printk("\n");
+       }
+}
+
+int
+bttv_risc_overlay(struct bttv *btv, struct bttv_riscmem *risc,
+                 const struct bttv_format *fmt, struct bttv_overlay *ov,
+                 int fields)
+{
+       int instructions,rc,line,maxy,start,end,skip,nskips;
+       struct SKIPLIST *skips;
+       unsigned long *rp,ri,ra;
+       unsigned long addr;
+
+       /* skip list for window clipping */
+       if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL)))
+               return -ENOMEM;
+       
+       /* estimate risc mem: worst case is (clip+1) * lines instructions
+          + sync + jump (all 2 dwords) */
+       instructions  = (ov->nclips + 1) *
+               ((fields & VBUF_FIELD_INTER) ? ov->height>>1 :  ov->height);
+       instructions += 2;
+       if ((rc = bttv_riscmem_alloc(btv->dev,risc,instructions*8)) < 0)
+               return rc;
+
+       /* sync instruction */
+       rp = risc->cpu;
+       *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
+       *(rp++) = cpu_to_le32(0);
+
+       addr  = (unsigned long)btv->fbuf.base;
+       addr += btv->fbuf.bytesperline * ov->y;
+       addr += ((btv->fbuf.depth+7) >> 3) * ov->x;
+
+       /* scan lines */
+       for (maxy = -1, line = 0; line < ov->height;
+            line++, addr += btv->fbuf.bytesperline) {
+               if (fields & VBUF_FIELD_INTER) {
+                       if ((line%2) != 0 && (fields & VBUF_FIELD_ODD))
+                               continue;
+                       if ((line%2) != 1 && (fields & VBUF_FIELD_EVEN))
+                               continue;
+               }
+
+               /* calculate clipping */
+               if (line > maxy)
+                       calc_skips(line, ov->width, &maxy,
+                                  skips, &nskips, ov->clips, ov->nclips);
+
+               /* write out risc code */
+               for (start = 0, skip = 0; start < ov->width; start = end) {
+                       if (skip >= nskips) {
+                               ri  = BT848_RISC_WRITE;
+                               end = ov->width;
+                       } else if (start < skips[skip].start) {
+                               ri  = BT848_RISC_WRITE;
+                               end = skips[skip].start;
+                       } else {
+                               ri  = BT848_RISC_SKIP;
+                               end = skips[skip].end;
+                               skip++;
+                       }
+                       if (BT848_RISC_WRITE == ri)
+                               ra = addr + (fmt->depth>>3)*start;
+                       else
+                               ra = 0;
+                               
+                       if (0 == start)
+                               ri |= BT848_RISC_SOL;
+                       if (ov->width == end)
+                               ri |= BT848_RISC_EOL;
+                       ri |= (fmt->depth>>3) * (end-start);
+
+                       *(rp++)=cpu_to_le32(ri);
+                       if (0 != ra)
+                               *(rp++)=cpu_to_le32(ra);
+               }
+       }
+
+       /* save pointer to jmp instruction address */
+       risc->jmp = rp;
+
+       kfree(skips);
+       return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+void
+bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo,
+             int width, int height, int interleaved, int norm)
+{
+       const struct bttv_tvnorm *tvnorm;
+        u32 xsf, sr;
+       int vdelay;
+
+       tvnorm = &bttv_tvnorms[norm];
+       vdelay = tvnorm->vdelay;
+       if (vdelay < btv->vbi.lines*2)
+               vdelay = btv->vbi.lines*2;
+
+        xsf = (width*tvnorm->scaledtwidth)/tvnorm->swidth;
+        geo->hscale =  ((tvnorm->totalwidth*4096UL)/xsf-4096);
+        geo->hdelay =  tvnorm->hdelayx1;
+        geo->hdelay =  (geo->hdelay*width)/tvnorm->swidth;
+        geo->hdelay &= 0x3fe;
+        sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512;
+        geo->vscale =  (0x10000UL-sr) & 0x1fff;
+        geo->crop   =  ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) |
+                ((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0);
+        geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0;
+        geo->vdelay  =  vdelay;
+        geo->width   =  width;
+        geo->sheight =  tvnorm->sheight;
+
+        if (btv->opt_combfilter) {
+                geo->vtc  = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
+                geo->comb = (width < 769) ? 1 : 0;
+        } else {
+                geo->vtc  = 0;
+                geo->comb = 0;
+        }
+}
+
+void
+bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd)
+{
+        int off = odd ? 0x80 : 0x00;
+
+       if (geo->comb)
+               btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+       else
+               btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
+
+        btwrite(geo->vtc,             BT848_E_VTC+off);
+        btwrite(geo->hscale >> 8,     BT848_E_HSCALE_HI+off);
+        btwrite(geo->hscale & 0xff,   BT848_E_HSCALE_LO+off);
+        btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
+        btwrite(geo->vscale & 0xff,   BT848_E_VSCALE_LO+off);
+        btwrite(geo->width & 0xff,    BT848_E_HACTIVE_LO+off);
+        btwrite(geo->hdelay & 0xff,   BT848_E_HDELAY_LO+off);
+        btwrite(geo->sheight & 0xff,  BT848_E_VACTIVE_LO+off);
+        btwrite(geo->vdelay & 0xff,   BT848_E_VDELAY_LO+off);
+        btwrite(geo->crop,            BT848_E_CROP+off);
+}
+
+/* ---------------------------------------------------------- */
+/* risc group / risc main loop / dma management               */
+
+void
+bttv_set_dma(struct bttv *btv, int override, int irqflags)
+{
+       unsigned long cmd;
+       int capctl;
+
+       btv->cap_ctl = 0;
+       if (NULL != btv->odd)      btv->cap_ctl |= 0x02;
+       if (NULL != btv->even)     btv->cap_ctl |= 0x01;
+       if (NULL != btv->vcurr)    btv->cap_ctl |= 0x0c;
+
+       capctl  = 0;
+       capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00;  /* capture  */
+       capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00;  /* vbi data */
+       capctl |= override;
+
+       d2printk(KERN_DEBUG
+                "bttv%d: capctl=%x irq=%d odd=%08Lx/%08Lx even=%08Lx/%08Lx\n",
+                btv->nr,capctl,irqflags,
+                btv->vcurr ? (u64)btv->vcurr->odd.dma  : 0,
+                btv->odd   ? (u64)btv->odd->odd.dma    : 0,
+                btv->vcurr ? (u64)btv->vcurr->even.dma : 0,
+                btv->even  ? (u64)btv->even->even.dma  : 0);
+       
+       cmd = BT848_RISC_JUMP;
+       if (irqflags) {
+               cmd |= BT848_RISC_IRQ | (irqflags << 16);
+               mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT);
+       } else {
+               del_timer(&btv->timeout);
+       }
+        btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd);
+       
+       btaor(capctl, ~0x0f, BT848_CAP_CTL);
+       if (capctl) {
+               if (btv->dma_on)
+                       return;
+               btwrite(btv->main.dma, BT848_RISC_STRT_ADD);
+               btor(3, BT848_GPIO_DMA_CTL);
+               btv->dma_on = 1;
+       } else {
+               if (!btv->dma_on)
+                       return;
+                btand(~3, BT848_GPIO_DMA_CTL);
+               btv->dma_on = 0;
+       }
+       return;
+}
+
+int
+bttv_risc_init_main(struct bttv *btv)
+{
+       int rc;
+       
+       if ((rc = bttv_riscmem_alloc(btv->dev,&btv->main,PAGE_SIZE)) < 0)
+               return rc;
+       dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n",
+               btv->nr,(u64)btv->main.dma);
+
+       btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+                                      BT848_FIFO_STATUS_VRE);
+       btv->main.cpu[1] = cpu_to_le32(0);
+       btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP);
+       btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2));
+
+       /* odd field */
+       btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP);
+       btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2));
+       btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP);
+       btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2));
+
+        btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC |
+                                      BT848_FIFO_STATUS_VRO);
+        btv->main.cpu[9] = cpu_to_le32(0);
+
+       /* even field */
+        btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP);
+       btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2));
+        btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP);
+       btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2));
+
+       /* jump back to odd field */
+       btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP);
+        btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2));
+
+       return 0;
+}
+
+int
+bttv_risc_hook(struct bttv *btv, int slot, struct bttv_riscmem *risc,
+              int irqflags)
+{
+       unsigned long cmd;
+       unsigned long next = btv->main.dma + ((slot+2) << 2);
+
+       if (NULL == risc) {
+               d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n",
+                        btv->nr,risc,slot);
+               btv->main.cpu[slot+1] = cpu_to_le32(next);
+       } else {
+               d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=%08Lx irq=%d\n",
+                        btv->nr,risc,slot,(u64)risc->dma,irqflags);
+               cmd = BT848_RISC_JUMP;
+               if (irqflags)
+                       cmd |= BT848_RISC_IRQ | (irqflags << 16);
+               risc->jmp[0] = cpu_to_le32(cmd);
+               risc->jmp[1] = cpu_to_le32(next);
+               btv->main.cpu[slot+1] = cpu_to_le32(risc->dma);
+       }
+       return 0;
+}
+
+void
+bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf)
+{
+       if (in_interrupt())
+               BUG();
+       videobuf_waiton(&buf->vb,0,0);
+       videobuf_dma_pci_unmap(btv->dev, &buf->vb.dma);
+       videobuf_dma_free(&buf->vb.dma);
+       bttv_riscmem_free(btv->dev,&buf->even);
+       bttv_riscmem_free(btv->dev,&buf->odd);
+       buf->vb.state = STATE_NEEDS_INIT;
+}
+
+int
+bttv_buffer_activate(struct bttv *btv,
+                    struct bttv_buffer *odd,
+                    struct bttv_buffer *even)
+{
+       if (NULL != odd  &&  NULL != even) {
+               odd->vb.state  = STATE_ACTIVE;
+               even->vb.state = STATE_ACTIVE;
+               bttv_apply_geo(btv, &odd->geo, 1);
+               bttv_apply_geo(btv, &even->geo,0);
+               bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &odd->odd,   0);
+               bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &even->even, 0);
+               btaor((odd->btformat & 0xf0) | (even->btformat & 0x0f),
+                     ~0xff, BT848_COLOR_FMT);
+               btaor((odd->btswap & 0x0a) | (even->btswap & 0x05),
+                     ~0x0f, BT848_COLOR_CTL);
+       } else if (NULL != odd) {
+               odd->vb.state  = STATE_ACTIVE;
+               bttv_apply_geo(btv, &odd->geo,1);
+               bttv_apply_geo(btv, &odd->geo,0);
+               bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &odd->odd, 0);
+               bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL,      0);
+               btaor(odd->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+               btaor(odd->btswap & 0x0f,   ~0x0f, BT848_COLOR_CTL);
+       } else if (NULL != even) {
+               even->vb.state = STATE_ACTIVE;
+               bttv_apply_geo(btv, &even->geo,1);
+               bttv_apply_geo(btv, &even->geo,0);
+               bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL,        0);
+               bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &even->even, 0);
+               btaor(even->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
+               btaor(even->btswap & 0x0f,   ~0x0f, BT848_COLOR_CTL);
+       } else {
+               bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
+               bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
+       }
+       return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+int
+bttv_buffer_field(struct bttv *btv, int field, int def_field,
+                 int norm, int height)
+{
+       const struct bttv_tvnorm *tvnorm = bttv_tvnorms + norm;
+
+       /* check interleave, even+odd fields */
+       if (height > (tvnorm->sheight >> 1)) {
+               field = VBUF_FIELD_ODD | VBUF_FIELD_EVEN | VBUF_FIELD_INTER;
+       } else {
+               if (field & def_field) {
+                       field = def_field;
+               } else {
+                       field = (def_field & VBUF_FIELD_EVEN)
+                               ? VBUF_FIELD_ODD : VBUF_FIELD_EVEN;
+               }
+       }
+       return field;
+}
+
+/* calculate geometry, build risc code */
+int
+bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
+{
+       const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm;
+
+       buf->vb.field = bttv_buffer_field(btv,buf->vb.field, VBUF_FIELD_EVEN,
+                                         buf->tvnorm,buf->vb.height);
+       dprintk(KERN_DEBUG
+               "bttv%d: buffer flags:%s%s%s format: %s size: %dx%d\n",
+               btv->nr,
+               (buf->vb.field & VBUF_FIELD_INTER) ? " interleave" : "",
+               (buf->vb.field & VBUF_FIELD_ODD)   ? " odd"        : "",
+               (buf->vb.field & VBUF_FIELD_EVEN)  ? " even"       : "",
+               buf->fmt->name,buf->vb.width,buf->vb.height);
+
+       /* packed pixel modes */
+       if (buf->fmt->flags & FORMAT_FLAGS_PACKED) {
+               int bpl, padding, lines;
+               bpl = (buf->fmt->depth >> 3) * buf->vb.width;
+
+               /* calculate geometry */
+               if (buf->vb.field & VBUF_FIELD_INTER) {
+                       bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+                                     buf->vb.height,1,buf->tvnorm);
+                       lines   = buf->vb.height >> 1;
+                       padding = bpl;
+               } else {
+                       bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+                                     buf->vb.height,0,buf->tvnorm);
+                       lines   = buf->vb.height;
+                       padding = 0;
+               }
+               
+               /* build risc code */
+               if (buf->vb.field & VBUF_FIELD_ODD)
+                       bttv_risc_packed(btv,&buf->odd,buf->vb.dma.sglist,
+                                        0,bpl,padding,lines);
+               if (buf->vb.field & VBUF_FIELD_EVEN)
+                       bttv_risc_packed(btv,&buf->even,buf->vb.dma.sglist,
+                                        padding,bpl,padding,lines);
+       }
+
+       /* planar modes */
+       if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) {
+               struct bttv_riscmem *risc;
+               int uoffset, voffset;
+               int ypadding, cpadding, lines;
+
+               /* calculate chroma offsets */
+               uoffset = buf->vb.width * buf->vb.height;
+               voffset = buf->vb.width * buf->vb.height;
+               if (buf->fmt->flags & FORMAT_FLAGS_CrCb) {
+                       /* Y-Cr-Cb plane order */
+                       uoffset >>= buf->fmt->hshift;
+                       uoffset >>= buf->fmt->vshift;
+                       uoffset += voffset;
+               } else {
+                       /* Y-Cb-Cr plane order */
+                       voffset >>= buf->fmt->hshift;
+                       voffset >>= buf->fmt->vshift;
+                       voffset += uoffset;
+               }
+
+               if (buf->vb.field & VBUF_FIELD_INTER) {
+                       bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+                                     buf->vb.height,1,buf->tvnorm);
+                       lines     = buf->vb.height >> 1;
+                       ypadding  = buf->vb.width;
+                       if (!buf->fmt->vshift) {
+                               /* chroma planes are interleaved too */
+                               cpadding = buf->vb.width >> buf->fmt->hshift;
+                       } else {
+                               cpadding = 0;
+                       }
+                       bttv_risc_planar(btv,&buf->odd,
+                                        buf->vb.dma.sglist,
+                                        0,buf->vb.width,ypadding,lines,
+                                        uoffset,voffset,buf->fmt->hshift,
+                                        buf->fmt->vshift >> 1,
+                                        cpadding);
+                       bttv_risc_planar(btv,&buf->even,
+                                        buf->vb.dma.sglist,
+                                        ypadding,buf->vb.width,ypadding,lines,
+                                        uoffset+cpadding,
+                                        voffset+cpadding,
+                                        buf->fmt->hshift,
+                                        cpadding ? (buf->fmt->vshift>>1) : -1,
+                                        cpadding);
+               } else {
+                       if (buf->vb.field & VBUF_FIELD_ODD)
+                               risc = &buf->odd;
+                       else
+                               risc = &buf->even;
+                       bttv_calc_geo(btv,&buf->geo,buf->vb.width,
+                                     buf->vb.height,0,buf->tvnorm);
+                       bttv_risc_planar(btv, risc, buf->vb.dma.sglist,
+                                        0,buf->vb.width,0,buf->vb.height,
+                                        uoffset,voffset,buf->fmt->hshift,
+                                        buf->fmt->vshift,0);
+               }
+       }
+
+       /* raw data */
+       if (buf->fmt->flags & FORMAT_FLAGS_RAW) {
+               /* build risc code */
+               buf->vb.field = VBUF_FIELD_INTER | VBUF_FIELD_ODD | VBUF_FIELD_EVEN;
+               bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
+                             1,buf->tvnorm);
+               bttv_risc_packed(btv, &buf->odd,  buf->vb.dma.sglist,
+                                0, RAW_BPL, 0, RAW_LINES);
+               bttv_risc_packed(btv, &buf->even, buf->vb.dma.sglist,
+                                buf->vb.size/2 , RAW_BPL, 0, RAW_LINES);
+       }
+
+       /* copy format info */
+       buf->btformat = buf->fmt->btformat;
+       buf->btswap   = buf->fmt->btswap;
+       return 0;
+}
+
+/* ---------------------------------------------------------- */
+
+/* calculate geometry, build risc code */
+int
+bttv_overlay_risc(struct bttv *btv,
+                 struct bttv_overlay *ov,
+                 const struct bttv_format *fmt,
+                 struct bttv_buffer *buf)
+{
+       int interleave;
+
+       /* check interleave, even+odd fields */
+       buf->vb.field = bttv_buffer_field(btv, 0, VBUF_FIELD_ODD,
+                                         ov->tvnorm,ov->height);
+       dprintk(KERN_DEBUG
+               "bttv%d: overlay flags:%s%s%s format: %s size: %dx%d\n",
+               btv->nr,
+               (buf->vb.field & VBUF_FIELD_INTER) ? " interleave" : "",
+               (buf->vb.field & VBUF_FIELD_ODD)   ? " odd" : "",
+               (buf->vb.field & VBUF_FIELD_EVEN)  ? " even" : "",
+               fmt->name,ov->width,ov->height);
+
+       /* calculate geometry */
+       interleave = buf->vb.field & VBUF_FIELD_INTER;
+       bttv_calc_geo(btv,&buf->geo,ov->width,ov->height,
+                     interleave, ov->tvnorm);
+
+       /* build risc code */
+       if (buf->vb.field & VBUF_FIELD_ODD)
+               bttv_risc_overlay(btv, &buf->odd, fmt, ov,
+                                 interleave | VBUF_FIELD_ODD);
+       if (buf->vb.field & VBUF_FIELD_EVEN)
+               bttv_risc_overlay(btv, &buf->even,fmt, ov,
+                                 interleave | VBUF_FIELD_EVEN);
+
+       /* copy format info */
+       buf->btformat = fmt->btformat;
+       buf->btswap   = fmt->btswap;
+       return 0;
+}
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
diff --git a/drivers/media/video/bttv-vbi.c b/drivers/media/video/bttv-vbi.c
new file mode 100644 (file)
index 0000000..ddef4c7
--- /dev/null
@@ -0,0 +1,658 @@
+/*
+    bttv - Bt848 frame grabber driver
+    vbi interface
+    
+    (c) 2002 Gerd Knorr <kraxel@bytesex.org>
+    
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+    
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+    
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kdev_t.h>
+#include <asm/io.h>
+#include "bttvp.h"
+
+#define VBI_DEFLINES 16
+#define VBI_MAXLINES 32
+
+static unsigned int vbibufs = 4;
+static unsigned int vbi_debug = 0;
+
+MODULE_PARM(vbibufs,"i");
+MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4");
+MODULE_PARM(vbi_debug,"i");
+MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)");
+
+#ifdef dprintk
+# undef dprintk
+#endif
+#define dprintk(fmt, arg...)   if (vbi_debug) \
+       printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->nr, ## arg)
+
+#ifndef HAVE_V4L2
+/* some dummy defines to avoid cluttering up the source code with
+   a huge number of ifdef's for V4L2 */
+# define V4L2_BUF_TYPE_CAPTURE -1
+# define V4L2_BUF_TYPE_VBI     -1
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* vbi risc code + mm                                                      */
+
+static int
+vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
+{
+       int bpl = 2048;
+
+       bttv_risc_packed(btv, &buf->odd, buf->vb.dma.sglist,
+                        0, bpl-4, 4, btv->vbi.lines);
+       bttv_risc_packed(btv, &buf->even, buf->vb.dma.sglist,
+                        btv->vbi.lines * bpl, bpl-4, 4, btv->vbi.lines);
+       return 0;
+}
+
+static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
+{
+       int rc;
+       
+       buf->vb.size = btv->vbi.lines * 2 * 2048;
+       if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
+               return -EINVAL;
+
+       if (STATE_NEEDS_INIT == buf->vb.state) {
+               if (0 != (rc = videobuf_iolock(btv->dev,&buf->vb)))
+                       goto fail;
+               if (0 != (rc = vbi_buffer_risc(btv,buf)))
+                       goto fail;
+       }
+       buf->vb.state = STATE_PREPARED;
+       dprintk("buf prepare ok: odd=%p even=%p\n",&buf->odd,&buf->even);
+       return 0;
+
+ fail:
+       bttv_dma_free(btv,buf);
+       return rc;
+}
+
+static void
+vbi_buffer_queue(struct bttv *btv, struct bttv_buffer *buf)
+{
+       unsigned long flags;
+       
+       buf->vb.state = STATE_QUEUED;
+       spin_lock_irqsave(&btv->s_lock,flags);
+       list_add_tail(&buf->vb.queue,&btv->vcapture);
+       bttv_set_dma(btv,0x0c,1);
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+}
+
+static void vbi_buffer_release(struct file *file, struct videobuf_buffer *vb)
+{
+       struct bttv *btv = file->private_data;
+       struct bttv_buffer *buf = (struct bttv_buffer*)vb;
+
+       bttv_dma_free(btv,buf);
+}
+
+static void
+vbi_cancel_all(struct bttv *btv)
+{
+       unsigned long flags;
+       int i;
+
+       /* remove queued buffers from list */
+       spin_lock_irqsave(&btv->s_lock,flags);
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == btv->vbi.bufs[i])
+                       continue;
+               if (btv->vbi.bufs[i]->vb.state == STATE_QUEUED) {
+                       list_del(&btv->vbi.bufs[i]->vb.queue);
+                       btv->vbi.bufs[i]->vb.state = STATE_ERROR;
+               }
+       }
+       spin_unlock_irqrestore(&btv->s_lock,flags);
+
+       /* free all buffers + clear queue */
+       for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+               if (NULL == btv->vbi.bufs[i])
+                       continue;
+               bttv_dma_free(btv,btv->vbi.bufs[i]);
+       }
+       INIT_LIST_HEAD(&btv->vbi.stream);
+}
+
+/* ----------------------------------------------------------------------- */
+
+static int vbi_read_start(struct file *file, struct bttv *btv)
+{
+       int err,size,count,i;
+       
+       if (vbibufs < 2 || vbibufs > VIDEO_MAX_FRAME)
+               vbibufs = 2;
+
+       count = vbibufs;
+       size  = btv->vbi.lines * 2 * 2048;
+       err = videobuf_mmap_setup(file,
+                                 (struct videobuf_buffer**)btv->vbi.bufs,
+                                 sizeof(struct bttv_buffer),
+                                 count,size,V4L2_BUF_TYPE_VBI,
+                                 vbi_buffer_release);
+       if (err)
+               return err;
+       for (i = 0; i < count; i++) {
+               err = vbi_buffer_prepare(btv,btv->vbi.bufs[i]);
+               if (err)
+                       return err;
+               list_add_tail(&btv->vbi.bufs[i]->vb.stream,&btv->vbi.stream);
+               vbi_buffer_queue(btv,btv->vbi.bufs[i]);
+       }
+       btv->vbi.reading = 1;
+       return 0;
+}
+
+static void vbi_read_stop(struct bttv *btv)
+{
+       int i;
+
+       vbi_cancel_all(btv);
+       INIT_LIST_HEAD(&btv->vbi.stream);
+       for (i = 0; i < vbibufs; i++) {
+               kfree(btv->vbi.bufs[i]);
+               btv->vbi.bufs[i] = NULL;
+       }
+       btv->vbi.reading = 0;
+}
+
+static void vbi_setlines(struct bttv *btv, int lines)
+{
+       int vdelay;
+
+       if (lines < 1)
+               lines = 1;
+       if (lines > VBI_MAXLINES)
+               lines = VBI_MAXLINES;
+       btv->vbi.lines = lines;
+
+       vdelay = btread(BT848_E_VDELAY_LO);
+       if (vdelay < lines*2) {
+               vdelay = lines*2;
+               btwrite(vdelay,BT848_E_VDELAY_LO);
+               btwrite(vdelay,BT848_O_VDELAY_LO);
+       }
+}
+
+#ifdef HAVE_V4L2
+static void vbi_fmt(struct bttv *btv, struct v4l2_format *f)
+{
+       memset(f,0,sizeof(*f));
+       f->type = V4L2_BUF_TYPE_VBI;
+       f->fmt.vbi.sampling_rate    = 35468950;
+       f->fmt.vbi.samples_per_line = 2048;
+       f->fmt.vbi.sample_format    = V4L2_VBI_SF_UBYTE;
+       f->fmt.vbi.offset           = 244;
+       f->fmt.vbi.count[0]         = btv->vbi.lines;
+       f->fmt.vbi.count[1]         = btv->vbi.lines;
+       f->fmt.vbi.flags            = 0;
+       switch (btv->tvnorm) {
+       case 1: /* NTSC */
+               f->fmt.vbi.start[0] = 10;
+               f->fmt.vbi.start[1] = 273;
+               break;
+       case 0: /* PAL */
+       case 2: /* SECAM */
+       default:
+               f->fmt.vbi.start[0] = 7;
+               f->fmt.vbi.start[1] = 319;
+       }
+}
+#endif
+
+/* ----------------------------------------------------------------------- */
+/* vbi interface                                                           */
+
+static int vbi_open(struct inode *inode, struct file *file)
+{
+       unsigned int minor = minor(inode->i_rdev);
+       struct bttv *btv = NULL;
+       int i;
+
+
+       for (i = 0; i < bttv_num; i++) {
+               if (bttvs[i].vbi_dev.minor == minor) {
+                       btv = &bttvs[i];
+                       break;
+               }
+       }
+       if (NULL == btv)
+               return -ENODEV;
+
+       down(&btv->vbi.lock);
+       if (btv->vbi.users) {
+               up(&btv->vbi.lock);
+               return -EBUSY;
+       }
+       dprintk("open minor=%d\n",minor);
+       file->private_data = btv;
+       btv->vbi.users++;
+       bttv_field_count(btv);
+       vbi_setlines(btv,VBI_DEFLINES);
+       
+       up(&btv->vbi.lock);
+       
+       return 0;
+}
+
+static int vbi_release(struct inode *inode, struct file *file)
+{
+       struct bttv    *btv = file->private_data;
+
+       down(&btv->vbi.lock);
+       if (btv->vbi.reading) {
+               vbi_read_stop(btv);
+               btv->vbi.read_buf = NULL;
+       }
+       btv->vbi.users--;
+       bttv_field_count(btv);
+       vbi_setlines(btv,0);
+       up(&btv->vbi.lock);
+       return 0;
+}
+
+static int vbi_ioctl(struct inode *inode, struct file *file,
+                    unsigned int cmd, void *arg)
+{
+       struct bttv *btv = file->private_data;
+#ifdef HAVE_V4L2
+       unsigned long flags;
+       int err;
+#endif
+       
+       if (btv->errors)
+               bttv_reinit_bt848(btv);
+       switch (cmd) {
+       case VIDIOCGCAP:
+       {
+                struct video_capability *cap = arg;
+
+               memset(cap,0,sizeof(*cap));
+                strcpy(cap->name,btv->vbi_dev.name);
+                cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT;
+                return 0;
+       }
+
+       /* vbi/teletext ioctls */
+       case BTTV_VBISIZE:
+               return btv->vbi.lines * 2 * 2048;
+
+       case BTTV_VERSION:
+        case VIDIOCGFREQ:
+        case VIDIOCSFREQ:
+        case VIDIOCGTUNER:
+        case VIDIOCSTUNER:
+        case VIDIOCGCHAN:
+        case VIDIOCSCHAN:
+               return bttv_common_ioctls(btv,cmd,arg);
+
+#ifdef HAVE_V4L2
+       case VIDIOC_QUERYCAP:
+       {
+               struct v4l2_capability *cap = arg;
+
+               memset(cap,0,sizeof(*cap));
+                strcpy(cap->name, btv->name);
+               cap->type = V4L2_TYPE_VBI;
+               cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_READ |
+                       V4L2_FLAG_STREAMING | V4L2_FLAG_SELECT;
+               return 0;
+       }
+       case VIDIOC_G_FMT:
+       {
+               struct v4l2_format *f = arg;
+
+               vbi_fmt(btv,f);
+               return 0;
+       }
+       case VIDIOC_S_FMT:
+       {
+               struct v4l2_format *f = arg;
+
+               if (btv->vbi.reading || btv->vbi.streaming)
+                       return -EBUSY;
+               vbi_setlines(btv,f->fmt.vbi.count[0]);
+               vbi_fmt(btv,f);
+               return 0;
+       }
+
+       case VIDIOC_REQBUFS:
+       {
+               struct v4l2_requestbuffers *req = arg;
+               int size,count;
+
+               if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
+                       return -EINVAL;
+               if (req->count < 1)
+                       return -EINVAL;
+
+               down(&btv->vbi.lock);
+               err = -EINVAL;
+
+               size  = btv->vbi.lines * 2 * 2048;
+               size  = (size + PAGE_SIZE - 1) & PAGE_MASK;
+               count = req->count;
+               if (count > VIDEO_MAX_FRAME)
+                       count = VIDEO_MAX_FRAME;
+               err = videobuf_mmap_setup(file,
+                                         (struct videobuf_buffer**)btv->vbi.bufs,
+                                         sizeof(struct bttv_buffer),
+                                         count,size,V4L2_BUF_TYPE_CAPTURE,
+                                         vbi_buffer_release);
+               if (err < 0)
+                       goto fh_unlock_and_return;
+               req->type  = V4L2_BUF_TYPE_VBI;
+               req->count = count;
+               up(&btv->vbi.lock);
+               return 0;
+       }
+       case VIDIOC_QUERYBUF:
+       {
+               struct v4l2_buffer *b = arg;
+
+               if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
+                       return -EINVAL;
+               if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
+                       return -EINVAL;
+               if (NULL == btv->vbi.bufs[b->index])
+                       return -EINVAL;
+               videobuf_status(b,&btv->vbi.bufs[b->index]->vb);
+               return 0;
+       }
+       case VIDIOC_QBUF:
+       {
+               struct v4l2_buffer *b = arg;
+               struct bttv_buffer *buf;
+               
+               if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
+                       return -EINVAL;
+               if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
+                       return -EINVAL;
+
+               down(&btv->vbi.lock);
+               err = -EINVAL;
+               buf = btv->vbi.bufs[b->index];
+               if (NULL == buf)
+                       goto fh_unlock_and_return;
+               if (0 == buf->vb.baddr)
+                       goto fh_unlock_and_return;
+               if (buf->vb.state == STATE_QUEUED ||
+                   buf->vb.state == STATE_ACTIVE)
+                       goto fh_unlock_and_return;
+               err = vbi_buffer_prepare(btv,buf);
+               if (0 != err)
+                       goto fh_unlock_and_return;
+
+               list_add_tail(&buf->vb.stream,&btv->vbi.stream);
+               if (btv->vbi.streaming)
+                       vbi_buffer_queue(btv,buf);
+               up(&btv->vbi.lock);
+               return 0;
+       }
+       case VIDIOC_DQBUF:
+       {
+               struct v4l2_buffer *b = arg;
+               struct bttv_buffer *buf;
+
+               if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
+                       return -EINVAL;
+
+               down(&btv->vbi.lock);
+               err = -EINVAL;
+               if (list_empty(&btv->vbi.stream))
+                       goto fh_unlock_and_return;
+               buf = list_entry(btv->vbi.stream.next,
+                                struct bttv_buffer, vb.stream);
+               err = videobuf_waiton(&buf->vb,0,1);
+               if (err < 0)
+                       goto fh_unlock_and_return;
+               switch (buf->vb.state) {
+               case STATE_ERROR:
+                       err = -EIO;
+                       /* fall through */
+               case STATE_DONE:
+                       videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
+                       buf->vb.state = STATE_IDLE;
+                       break;
+               default:
+                       err = -EINVAL;
+                       goto fh_unlock_and_return;
+               }
+               list_del(&buf->vb.stream);
+               memset(b,0,sizeof(*b));
+               videobuf_status(b,&buf->vb);
+               up(&btv->vbi.lock);
+               return err;
+       }
+       case VIDIOC_STREAMON:
+       {
+               struct list_head *list;
+               struct bttv_buffer *buf;
+
+               down(&btv->vbi.lock);
+               err = -EBUSY;
+               if (btv->vbi.reading)
+                       goto fh_unlock_and_return;
+               spin_lock_irqsave(&btv->s_lock,flags);
+               list_for_each(list,&btv->vbi.stream) {
+                       buf = list_entry(list, struct bttv_buffer, vb.stream);
+                       if (buf->vb.state == STATE_PREPARED)
+                               vbi_buffer_queue(btv,buf);
+               }
+               spin_unlock_irqrestore(&btv->s_lock,flags);
+               btv->vbi.streaming = 1;
+               up(&btv->vbi.lock);
+               return 0;
+       }
+       case VIDIOC_STREAMOFF:
+       {
+               down(&btv->vbi.lock);
+               err = -EINVAL;
+               if (!btv->vbi.streaming)
+                       goto fh_unlock_and_return;
+               vbi_cancel_all(btv);
+               INIT_LIST_HEAD(&btv->vbi.stream);
+               btv->vbi.streaming = 0;
+               up(&btv->vbi.lock);
+               return 0;
+       }
+
+       case VIDIOC_ENUMSTD:
+       case VIDIOC_G_STD:
+       case VIDIOC_S_STD:
+       case VIDIOC_ENUMINPUT:
+       case VIDIOC_G_INPUT:
+       case VIDIOC_S_INPUT:
+       case VIDIOC_G_TUNER:
+       case VIDIOC_S_TUNER:
+       case VIDIOC_G_FREQ:
+       case VIDIOC_S_FREQ:
+               return bttv_common_ioctls(btv,cmd,arg);
+#endif /* HAVE_V4L2 */
+
+       default:
+               return -ENOIOCTLCMD;
+       }
+       return 0;
+
+#ifdef HAVE_V4L2
+ fh_unlock_and_return:
+       up(&btv->vbi.lock);
+       return err;
+#endif
+}
+
+static ssize_t vbi_read(struct file *file, char *data,
+                       size_t count, loff_t *ppos)
+{
+       unsigned int *fc;
+       struct bttv *btv = file->private_data;
+       int err, bytes, retval = 0;
+
+       if (btv->errors)
+               bttv_reinit_bt848(btv);
+       down(&btv->vbi.lock);
+       if (!btv->vbi.reading) {
+               retval = vbi_read_start(file,btv);
+               if (retval < 0)
+                       goto done;
+       }
+
+       while (count > 0) {
+               /* get / wait for data */
+               if (NULL == btv->vbi.read_buf) {
+                       btv->vbi.read_buf = list_entry(btv->vbi.stream.next,
+                                                      struct bttv_buffer,
+                                                      vb.stream);
+                       list_del(&btv->vbi.read_buf->vb.stream);
+                       btv->vbi.read_off = 0;
+               }
+               err = videobuf_waiton(&btv->vbi.read_buf->vb,
+                                     file->f_flags & O_NONBLOCK,1);
+               if (err < 0) {
+                       if (0 == retval)
+                               retval = err;
+                       break;
+               }
+
+#if 1
+               /* dirty, undocumented hack -- pass the frame counter
+                * within the last four bytes of each vbi data block.
+                * We need that one to maintain backward compatibility
+                * to all vbi decoding software out there ... */
+               fc  = (unsigned int*)btv->vbi.read_buf->vb.dma.vmalloc;
+               fc += (btv->vbi.read_buf->vb.size>>2) -1;
+               *fc = btv->vbi.read_buf->vb.field_count >> 1;
+#endif
+
+               /* copy stuff */
+               bytes = count;
+               if (bytes > btv->vbi.read_buf->vb.size - btv->vbi.read_off)
+                       bytes = btv->vbi.read_buf->vb.size - btv->vbi.read_off;
+               if (copy_to_user(data + retval,btv->vbi.read_buf->vb.dma.vmalloc +
+                                btv->vbi.read_off,bytes)) {
+                       if (0 == retval)
+                               retval = -EFAULT;
+                       break;
+               }
+               count             -= bytes;
+               retval            += bytes;
+               btv->vbi.read_off += bytes;
+               dprintk("read: %d bytes\n",bytes);
+
+               /* requeue buffer when done with copying */
+               if (btv->vbi.read_off == btv->vbi.read_buf->vb.size) {
+                       list_add_tail(&btv->vbi.read_buf->vb.stream,
+                                     &btv->vbi.stream);
+                       vbi_buffer_queue(btv,btv->vbi.read_buf);
+                       btv->vbi.read_buf = NULL;
+               }
+       }
+ done:
+       up(&btv->vbi.lock);
+       return retval;
+}
+
+static unsigned int vbi_poll(struct file *file, poll_table *wait)
+{
+       struct bttv *btv = file->private_data;
+       struct bttv_buffer *buf = NULL;
+       unsigned int rc = 0;
+
+       down(&btv->vbi.lock);
+       if (btv->vbi.streaming) {
+               if (!list_empty(&btv->vbi.stream))
+                       buf = list_entry(btv->vbi.stream.next,
+                                        struct bttv_buffer, vb.stream);
+       } else {
+               if (!btv->vbi.reading)
+                       vbi_read_start(file,btv);
+               if (!btv->vbi.reading) {
+                       rc = POLLERR;
+               } else if (NULL == btv->vbi.read_buf) {
+                       btv->vbi.read_buf = list_entry(btv->vbi.stream.next,
+                                                      struct bttv_buffer,
+                                                      vb.stream);
+                       list_del(&btv->vbi.read_buf->vb.stream);
+                       btv->vbi.read_off = 0;
+               }
+               buf = btv->vbi.read_buf;
+       }
+       if (!buf)
+               rc = POLLERR;
+
+       if (0 == rc) {
+               poll_wait(file, &buf->vb.done, wait);
+               if (buf->vb.state == STATE_DONE ||
+                   buf->vb.state == STATE_ERROR)
+                       rc = POLLIN|POLLRDNORM;
+       }
+       up(&btv->vbi.lock);
+       return rc;
+}
+
+static int
+vbi_mmap(struct file *file, struct vm_area_struct * vma)
+{
+       struct bttv *btv = file->private_data;
+       int err;
+
+       down(&btv->vbi.lock);
+       err = videobuf_mmap_mapper
+               (vma,(struct videobuf_buffer**)btv->vbi.bufs);
+       up(&btv->vbi.lock);
+       return err;
+}
+
+static struct file_operations vbi_fops =
+{
+       owner:    THIS_MODULE,
+       open:     vbi_open,
+       release:  vbi_release,
+       ioctl:    video_generic_ioctl,
+       llseek:   no_llseek,
+       read:     vbi_read,
+       poll:     vbi_poll,
+       mmap:     vbi_mmap,
+};
+
+struct video_device bttv_vbi_template =
+{
+       name:     "bt848/878 vbi",
+       type:     VID_TYPE_TUNER|VID_TYPE_TELETEXT,
+       hardware: VID_HARDWARE_BT848,
+       fops:     &vbi_fops,
+       kernel_ioctl: vbi_ioctl,
+       minor:    -1,
+};
+
+/*
+ * Local variables:
+ * c-basic-offset: 8
+ * End:
+ */
index ce3dc51db593846e9324ebdb5f61e5919e66c5ac..715e6bdf6927d3703828ded25f9ab84547696f93 100644 (file)
@@ -87,7 +87,9 @@
 #define BTTV_PV_BT878P_PLUS 0x46
 #define BTTV_FLYVIDEO98EZ   0x47
 #define BTTV_PV_BT878P_9B   0x48
-
+#define BTTV_SENSORAY311    0x49
+#define BTTV_RV605          0x4a
+#define BTTV_WINDVR         0x4c
 
 /* i2c address list */
 #define I2C_TSA5522        0xc2
@@ -95,7 +97,7 @@
 #define I2C_TDA8425        0x82
 #define I2C_TDA9840        0x84
 #define I2C_TDA9850        0xb6 /* also used by 9855,9873 */
-#define I2C_TDA9874A       0xb0 /* also used by 9875 */
+#define I2C_TDA9874        0xb0 /* also used by 9875 */
 #define I2C_TDA9875        0xb0
 #define I2C_HAUPEE         0xa0
 #define I2C_STBEE          0xae
@@ -123,7 +125,7 @@ struct tvcard
         int tuner;
         int svhs;
         u32 gpiomask;
-        u32 muxsel[8];
+        u32 muxsel[16];
         u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */
         u32 gpiomask2;   /* GPIO MUX mask */
 
@@ -141,6 +143,7 @@ struct tvcard
        int tuner_type;
        int has_radio;
        void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
+       void (*muxsel_hook)(struct bttv *btv, unsigned int input);
 };
 
 extern struct tvcard bttv_tvcards[];
@@ -148,11 +151,12 @@ extern const int bttv_num_tvcards;
 
 /* identification / initialization of the card */
 extern void bttv_idcard(struct bttv *btv);
-extern void bttv_init_card(struct bttv *btv);
+extern void bttv_init_card1(struct bttv *btv);
+extern void bttv_init_card2(struct bttv *btv);
 
 /* card-specific funtions */
 extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
-extern void bttv_boot_msp34xx(struct bttv *btv, int pin);
+extern void bttv_tda9880_setnorm(struct bttv *btv, int norm);
 
 /* kernel cmd line parse helper */
 extern int bttv_parse(char *str, int max, int *vals);
index 15745818877ad206da78ef4b7ceea19ceb55150a..35294943379d879677467c6ff8b313daed668a11 100644 (file)
@@ -1,11 +1,10 @@
 /*
     bttv - Bt848 frame grabber driver
 
-    bttv's *private* header file  --  nobody else than bttv itself
+    bttv's *private* header file  --  nobody other than bttv itself
     should ever include this file.
 
-    Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
-    (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
+    (c) 2000-2002 Gerd Knorr <kraxel@bytesex.org>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #ifndef _BTTVP_H_
 #define _BTTVP_H_
 
-#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,83)
-
+#define BTTV_VERSION_CODE KERNEL_VERSION(0,8,38)
 
 #include <linux/types.h>
 #include <linux/wait.h>
 #include <linux/i2c.h>
 #include <linux/i2c-algo-bit.h>
+#include <linux/videodev.h>
+#include <linux/iobuf.h>
+#include <linux/pci.h>
+#include <asm/scatterlist.h>
 
 #include "bt848.h"
 #include "bttv.h"
+#include "video-buf.h"
 #include "audiochip.h"
 
 #ifdef __KERNEL__
 
+#define FORMAT_FLAGS_DITHER       0x01
+#define FORMAT_FLAGS_PACKED       0x02
+#define FORMAT_FLAGS_PLANAR       0x04
+#define FORMAT_FLAGS_RAW          0x08
+#define FORMAT_FLAGS_CrCb         0x10
+
+#define RISC_SLOT_O_VBI        4
+#define RISC_SLOT_O_FIELD      6
+#define RISC_SLOT_E_VBI       10
+#define RISC_SLOT_E_FIELD     12
+#define RISC_SLOT_LOOP        14
+
+#define RESOURCE_OVERLAY       1
+#define RESOURCE_STREAMING     2
+
+#define RAW_LINES            640
+#define RAW_BPL             1024
+
+/* ---------------------------------------------------------- */
+
+struct bttv_tvnorm 
+{
+       int v4l2_id;
+        u32 Fsc;
+        u16 swidth, sheight; /* scaled standard width, height */
+       u16 totalwidth;
+       u8 adelay, bdelay, iform;
+       u32 scaledtwidth;
+       u16 hdelayx1, hactivex1;
+       u16 vdelay;
+        u8 vbipack;
+};
+extern const struct bttv_tvnorm bttv_tvnorms[];
+extern const int BTTV_TVNORMS;
+
+struct bttv_format {
+       char *name;
+       int  palette;         /* video4linux 1      */
+       int  fourcc;          /* video4linux 2      */
+       int  btformat;        /* BT848_COLOR_FMT_*  */
+       int  btswap;          /* BT848_COLOR_CTL_*  */
+       int  depth;           /* bit/pixel          */
+       int  flags;
+       int  hshift,vshift;   /* for planar modes   */
+};
+extern const struct bttv_format bttv_formats[];
+extern const int BTTV_FORMATS;
+
+/* ---------------------------------------------------------- */
+
+struct bttv_geometry {
+       u8  vtc,crop,comb;
+       u16 width,hscale,hdelay;
+       u16 sheight,vscale,vdelay;
+};
+
+struct bttv_riscmem {
+       unsigned int   size;
+       unsigned long  *cpu;
+       unsigned long  *jmp;
+       dma_addr_t     dma;
+};
+
+struct bttv_buffer {
+       /* common v4l buffer stuff -- must be first */
+       struct videobuf_buffer     vb;
+
+       /* bttv specific */
+       const struct bttv_format   *fmt;
+       int                        tvnorm;
+       int                        btformat;
+       int                        btswap;
+       struct bttv_geometry       geo;
+       struct bttv_riscmem        even;
+       struct bttv_riscmem        odd;
+};
+
+struct bttv_overlay {
+       int tvnorm;
+       int x,y,width,height;
+       struct video_clip      *clips;
+       int                    nclips;
+};
+
+struct bttv_vbi {
+        struct semaphore           lock;
+       int                        users;
+       int                        lines;
+
+       /* mmap */
+       int                        streaming;
+       struct bttv_buffer         *bufs[VIDEO_MAX_FRAME];
+       struct list_head           stream;
+
+       /* read */
+       int                        reading;
+       int                        read_off;
+       struct bttv_buffer         *read_buf;
+};
+
+struct bttv_fh {
+       struct bttv              *btv;
+
+       /* locking */
+       int resources;
+       struct semaphore lock;
+
+       /* keep current driver settings */
+       const struct bttv_format *ovfmt;
+       struct bttv_overlay      ov;
+       struct bttv_buffer       buf;
+
+       /* for read() capture */
+       struct bttv_buffer       read_buf;
+       int                      read_off;
+
+       /* mmap()'ed buffers */
+       struct bttv_buffer       *bufs[VIDEO_MAX_FRAME];
+       struct list_head stream; /* v4l2 QBUF/DQBUF */
+};
+
+/* ---------------------------------------------------------- */
+/* bttv-risc.c                                                */
+
+/* alloc/free memory */
+int  bttv_riscmem_alloc(struct pci_dev *pci,
+                       struct bttv_riscmem *risc,
+                       unsigned int size);
+void bttv_riscmem_free(struct pci_dev *pci,
+                      struct bttv_riscmem *risc);
+
+/* risc code generators - capture */
+int bttv_risc_packed(struct bttv *btv, struct bttv_riscmem *risc,
+                    struct scatterlist *sglist,
+                    int offset, int bpl, int pitch, int lines);
+int bttv_risc_planar(struct bttv *btv, struct bttv_riscmem *risc,
+                    struct scatterlist *sglist,
+                    int yoffset, int ybpl, int ypadding, int ylines,
+                    int uoffset, int voffset, int hshift, int vshift,
+                    int cpadding);
+
+/* risc code generator + helpers - screen overlay */
+int bttv_screen_clips(struct video_buffer *fbuf,
+                     int x, int y, int width, int height,
+                     struct video_clip *clips, int n);
+void bttv_sort_clips(struct video_clip *clips, int nclips);
+int bttv_risc_overlay(struct bttv *btv, struct bttv_riscmem *risc,
+                     const struct bttv_format *fmt,
+                     struct bttv_overlay *ov, int flags);
+
+/* calculate / apply geometry settings */
+void bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo,
+                  int width, int height, int interleaved, int norm);
+void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd);
+
+/* control dma register + risc main loop */
+void bttv_set_dma(struct bttv *btv, int override, int irqflags);
+int bttv_risc_init_main(struct bttv *btv);
+int bttv_risc_hook(struct bttv *btv, int slot, struct bttv_riscmem *risc,
+                  int irqflags);
+
+/* capture buffer handling */
+int bttv_buffer_field(struct bttv *btv, int field, int def_field,
+                     int tvnorm, int height);
+int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf);
+int bttv_buffer_activate(struct bttv *btv, struct bttv_buffer *odd,
+                        struct bttv_buffer *even);
+void bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf);
+
+/* overlay handling */
+int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
+                     const struct bttv_format *fmt,
+                     struct bttv_buffer *buf);
+
+
+/* ---------------------------------------------------------- */
+/* bttv-vbi.c                                                 */
+
+extern struct video_device bttv_vbi_template;
+
 /* ---------------------------------------------------------- */
 /* bttv-driver.c                                              */
 
-/* insmod options / kernel args */
-extern int no_overlay;
+/* insmod options */
 extern unsigned int bttv_verbose;
 extern unsigned int bttv_debug;
 extern unsigned int bttv_gpio;
 extern void bttv_gpio_tracking(struct bttv *btv, char *comment);
 extern int init_bttv_i2c(struct bttv *btv);
 
-#define dprintk                if (bttv_debug) printk
+extern int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg);
+extern void bttv_reinit_bt848(struct bttv *btv);
+extern void bttv_field_count(struct bttv *btv);
+
+#define vprintk  if (bttv_verbose) printk
+#define dprintk  if (bttv_debug >= 1) printk
+#define d2printk if (bttv_debug >= 2) printk
 
-/* Anybody who uses more than four? */
+/* our devices */
 #define BTTV_MAX 4
-extern int bttv_num;                   /* number of Bt848s in use */
+extern int bttv_num;
 extern struct bttv bttvs[BTTV_MAX];
 
-
-#ifndef O_NONCAP  
-#define O_NONCAP       O_TRUNC
-#endif
-
-#ifdef VIDEODAT_HACK
-# define VBI_MAXLINES   19
-#else
-# define VBI_MAXLINES   16
-#endif
+#define BTTV_MAX_FBUF   0x208000
 #define VBIBUF_SIZE     (2048*VBI_MAXLINES*2)
-#define MAX_GBUFFERS   64
-#define RISCMEM_LEN    (32744*2)
+#define BTTV_TIMEOUT    (HZ/2) /* 0.5 seconds */
+#define BTTV_FREE_IDLE  (HZ)   /* one second */
 
-#define BTTV_MAX_FBUF  0x208000
-
-struct bttv_window 
-{
-       int x, y;
-       ushort width, height;
-       ushort bpp, bpl;
-       ushort swidth, sheight;
-       unsigned long vidadr;
-       ushort freq;
-       int norm;
-       int interlace;
-       int color_fmt;
-       ushort depth;
-};
 
 struct bttv_pll_info {
-       unsigned int pll_ifreq;    /* PLL input frequency        */
-       unsigned int pll_ofreq;    /* PLL output frequency       */
+       unsigned int pll_ifreq;    /* PLL input frequency        */
+       unsigned int pll_ofreq;    /* PLL output frequency       */
        unsigned int pll_crystal;  /* Crystal used for input     */
        unsigned int pll_current;  /* Currently programmed ofreq */
 };
 
-struct bttv_gbuf {
-       int stat;
-#define GBUFFER_UNUSED       0
-#define GBUFFER_GRABBING     1
-#define GBUFFER_DONE         2
-#define GBUFFER_ERROR        3
-       struct timeval tv;
-       
-       u16 width;
-       u16 height;
-       u16 fmt;
-       
-       u32 *risc;
-       unsigned long ro;
-       unsigned long re;
-};
-
 struct bttv {
-       struct video_device video_dev;
-       struct video_device radio_dev;
-       struct video_device vbi_dev;
-       struct video_picture picture;           /* Current picture params */
-       struct video_audio audio_dev;           /* Current audio params */
+       /* pci device config */
+       struct pci_dev *dev;
+       unsigned short id;
+       unsigned char revision;
+       unsigned char *bt848_mmio;   /* pointer to mmio */
+
+       /* card configuration info */
+        unsigned int nr;       /* dev nr (for printk("bttv%d: ...");  */
+       char name[8];          /* dev name */
+       int cardid;            /* pci subsystem id (bt878 based ones) */
+       int type;              /* card type (pointer into tvcards[])  */
+        int tuner_type;        /* tuner chip type */
+       struct bttv_pll_info pll;
+       int triton1;
 
-       spinlock_t s_lock;
-        struct semaphore lock;
-       int user;
-       int capuser;
+       /* gpio interface */
+       wait_queue_head_t gpioq;
+       int shutdown;
 
-       /* i2c */
+       /* i2c layer */
        struct i2c_adapter         i2c_adap;
        struct i2c_algo_bit_data   i2c_algo;
        struct i2c_client          i2c_client;
        int                        i2c_state, i2c_rc;
        struct i2c_client         *i2c_clients[I2C_CLIENTS_MAX];
 
-        int tuner_type;
-        int channel;
-        
-        unsigned int nr;
-       unsigned short id;
-       struct pci_dev *dev;
-       unsigned int irq;          /* IRQ used by Bt848 card */
-       unsigned char revision;
-       unsigned long bt848_adr;      /* bus address of IO mem returned by PCI BIOS */
-       unsigned char *bt848_mem;   /* pointer to mapped IO memory */
-       unsigned long busriscmem; 
-       u32 *riscmem;
-  
-       unsigned char *vbibuf;
-       struct bttv_window win;
-       int fb_color_ctl;
-       int type;            /* card type  */
-       int cardid;
-       int audio;           /* audio mode */
-       int audio_chip;      /* set to one of the chips supported by bttv.c */
-       int radio;
-       int has_radio;
+       /* video4linux (1) */
+       struct video_device video_dev;
+       struct video_device radio_dev;
+       struct video_device vbi_dev;
 
-       /* miro/pinnacle + Aimslab VHX
-          philips matchbox (tea5757 radio tuner) support */
+       /* locking */
+       spinlock_t s_lock;
+        struct semaphore lock;
+       int resources;
+        struct semaphore reslock;
+
+       /* video state */
+       int input;
+       int audio;
+       unsigned long freq;
+       int tvnorm,hue,contrast,bright,saturation;
+       struct video_buffer fbuf;
+       int field_count;
+
+       /* various options */
+       int opt_combfilter;
+       int opt_lumafilter;
+       int opt_automute;
+       int opt_chroma_agc;
+       int opt_adc_crush;
+
+       /* vbi data/state */
+       struct bttv_vbi vbi;
+
+       /* radio data/state */
+       int has_radio;
        int has_matchbox;
-       int mbox_we;
+        int mbox_we;
        int mbox_data;
        int mbox_clk;
        int mbox_most;
        int mbox_mask;
-
-       u32 *risc_jmp;
-       u32 *vbi_odd;
-       u32 *vbi_even;
-       u32 bus_vbi_even;
-       u32 bus_vbi_odd;
-        wait_queue_head_t vbiq;
-       wait_queue_head_t capq;
-       int vbip;
-
-       u32 *risc_scr_odd;
-       u32 *risc_scr_even;
-       u32 risc_cap_odd;
-       u32 risc_cap_even;
-       int scr_on;
-       int vbi_on;
-       struct video_clip *cliprecs;
-
-       struct bttv_gbuf *gbuf;
-       int gqueue[MAX_GBUFFERS];
-       int gq_in,gq_out,gq_grab,gq_start;
-        char *fbuffer;
-
-       struct bttv_pll_info pll;
-       unsigned int Fsc;
-       unsigned int field;
-       unsigned int last_field; /* number of last grabbed field */
-       int i2c_command;
-       int triton1;
-
+       int radio_user;
+       
+       /* risc memory management data
+          - must aquire s_lock before changing these
+          - only the irq handler is supported to touch odd + even */
+       struct bttv_riscmem    main;
+       struct bttv_buffer     *odd;       /* current active odd field   */
+       struct bttv_buffer     *even;      /* current active even field  */
+       struct bttv_buffer     *screen;    /* overlay                    */
+       struct list_head       capture;    /* capture buffer queue       */
+       struct bttv_buffer     *vcurr;
+       struct list_head       vcapture;
+
+       unsigned long cap_ctl;
+       unsigned long dma_on;
+       struct timer_list timeout;
        int errors;
-       int needs_restart;
 
-       wait_queue_head_t gpioq;
-       int shutdown;
+       int user;
+       struct bttv_fh init;
 };
+
+/* private ioctls */
+#define BTTV_VERSION            _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
+#define BTTV_VBISIZE            _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
+
 #endif
 
-#define btwrite(dat,adr)    writel((dat), (char *) (btv->bt848_mem+(adr)))
-#define btread(adr)         readl(btv->bt848_mem+(adr))
+#define btwrite(dat,adr)    writel((dat), (char *) (btv->bt848_mmio+(adr)))
+#define btread(adr)         readl(btv->bt848_mmio+(adr))
 
 #define btand(dat,adr)      btwrite((dat) & btread(adr), adr)
 #define btor(dat,adr)       btwrite((dat) | btread(adr), adr)
 #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr)
 
-/* bttv ioctls */
-
-#define BTTV_READEE            _IOW('v',  BASE_VIDIOCPRIVATE+0, char [256])
-#define BTTV_WRITEE            _IOR('v',  BASE_VIDIOCPRIVATE+1, char [256])
-#define BTTV_FIELDNR           _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int)
-#define BTTV_PLLSET            _IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info)
-#define BTTV_BURST_ON          _IOR('v' , BASE_VIDIOCPRIVATE+4, int)
-#define BTTV_BURST_OFF         _IOR('v' , BASE_VIDIOCPRIVATE+5, int)
-#define BTTV_VERSION           _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
-#define BTTV_PICNR             _IOR('v' , BASE_VIDIOCPRIVATE+7, int)
-#define BTTV_VBISIZE            _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
-
-#define TDA9850            0x01
-#define TDA9840            0x02
-#define TDA8425            0x03
-#define TEA6300            0x04
-
 #endif /* _BTTVP_H_ */
 
 /*