Subversion Repositories shark

Rev

Rev 179 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
    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>

    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.
*/


/* SHARK version by Giacomo Guidi <giacomo@gandalf.sssup.it> */

#include <sys/types.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/pci.h>

#include "drivers/compbttv.h"
#include "drivers/bttv.h"
#include "drivers/tuner.h"

#define DEBUG(x)        /* Debug driver */
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)>(b))?(a):(b))

static inline int copy_to_user (void *s, void *d, size_t size)
{
        memcpy(s,d,size);
        return 0;

}

static inline int copy_from_user (void *s, void *d, size_t size)
{
        memcpy(s,d,size);
        return 0;

}

static void bt848_set_risc_jmps(struct bttv *btv, int state);

int bttv_num;                   /* number of Bt848s in use */
int in_irq = 0;
struct bttv *btvirq;
struct bttv bttvs[BTTV_MAX];

/* configuration 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 irq_debug = 0;
static unsigned int gbuffers = 2;
unsigned int gbufsize = BTTV_MAX_FBUF;
static int latency = -1;

static unsigned int combfilter = 0;
static unsigned int lumafilter = 0;
static unsigned int chroma_agc = 0;
static unsigned int adc_crush = 1;
unsigned int bttv_debug = 0;
unsigned int bttv_verbose = 1;
unsigned int bttv_gpio = 0;

//#define I2C_TIMING (0x7<<4)
//#define I2C_DELAY   10

#define I2C_TIMING (0x3<<4)
#define I2C_DELAY   40

#define I2C_SET(CTRL,DATA) \
    { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); }

#define I2C_GET()   (btread(BT848_I2C)&1)

#define BURSTOFFSET 76
#define BTTV_ERRORS 5


/*******************************/
/* Memory management functions */
/*******************************/

#define MDEBUG(x)       do { } while(0)         /* Debug memory management */

/* [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.
 */



static inline unsigned long cpu_to_le32(unsigned long adr)
{
        return adr;
}

static inline unsigned long virt_to_bus(unsigned long adr)
{
        return (unsigned long)(adr);
}

static void * vmalloc_32(size_t size)
{
        void *mem;
        unsigned long diff;

        mem = malloc(size+8);

        diff = (unsigned long)((((unsigned long)mem/4)+1)*4-(unsigned long)mem);
       
        *(unsigned long *)(mem+diff) = (unsigned long)diff;
       
        return (mem+diff+4);
       
}

static void vfree_32(void *ptr)
{

        free(ptr-4-*(unsigned long *)(ptr-4));

}
/*
 *      Create the giant waste of buffer space we need for now
 *      until we get DMA to user space sorted out (probably 2.3.x)
 *
 *      We only create this as and when someone uses mmap
 */

 
static int fbuffer_alloc(struct bttv *btv)
{
        if(!btv->fbuffer) {
                btv->fbuffer=(unsigned char *) vmalloc_32(gbuffers*gbufsize);
                memset(btv->fbuffer, 0, 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    = gpioread();
        printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
               btv->nr,outbits,data & outbits, data & ~outbits, comment);
}

static inline void bt848_dma(struct bttv *btv, uint state)
{
        if (state)
                btor(3, BT848_GPIO_DMA_CTL);
        else
                btand(~3, BT848_GPIO_DMA_CTL);
}


/* 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)
   PLL_C = Post divider (0=6, 1=4)
   PLL_I = Integer input
   PLL_F = Fractional input
   
   F_input = 28.636363 MHz:
   PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
*/


static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
{
        unsigned char fl, fh, fi;
       
        /* prevent overflows */
        fin/=4;
        fout/=4;

        fout*=12;
        fi=fout/fin;

        fout=(fout%fin)*256;
        fh=fout/fin;

        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);
}

static int set_pll(struct bttv *btv)
{
        int i;

        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); */
                        return 0;
                }
                if (bttv_verbose)
                        printk(KERN_INFO "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)
                cprintf("[info  ] 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 */
        udelay(100000);
       
        for (i=0; i<100; i++)
        {
                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)
                                cprintf("ok\n");
                        return 1;
                }
                udelay(10000);
        }
        btv->pll.pll_current = 0;
        if (bttv_verbose)
                cprintf("oops\n");
        return -1;
}

static void bt848_muxsel(struct bttv *btv, unsigned int input)
{
        /* needed by RemoteVideo MX */
        btor(bttv_tvcards[btv->type].gpiomask2,BT848_GPIO_OUT_EN);

        /* This seems to get rid of some synchronization problems */
        btand(~(3<<5), BT848_IFORM);
        udelay(10000);

        input %= bttv_tvcards[btv->type].video_inputs;
        if (input==bttv_tvcards[btv->type].svhs)
        {
                btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
                btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
        }
        else
        {
                btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
                btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
        }

        btaor((bttv_tvcards[btv->type].muxsel[input]&3)<<5, ~(3<<5), BT848_IFORM);

        gpioaor(bttv_tvcards[btv->type].muxsel[input]>>4,
                ~bttv_tvcards[btv->type].gpiomask2);

        /* card specific hook */
        if( bttv_tvcards[btv->type].muxsel_hook )
                bttv_tvcards[btv->type].muxsel_hook ( btv, input );

        if (bttv_gpio)
                bttv_gpio_tracking(btv,"muxsel");

}


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 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

/* RISC command to write one VBI data line */
#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL

static void make_vbitab(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)
                cprintf("bttv%d: vbi1: po=%08lx pe=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)po), virt_to_bus((unsigned long)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(virt_to_bus((unsigned long)btv->vbibuf+i*2048));
        }
        *(po++)=cpu_to_le32(BT848_RISC_JUMP);
        *(po++)=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+16));

        *(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(virt_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((unsigned long)btv->risc_jmp+40));

        if (bttv_debug > 1)
                cprintf("bttv%d: vbi2: po=%08lx pe=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)po), virt_to_bus((unsigned long)pe));
}

static int fmtbppx2[16] = {
        8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0
};

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))

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(virt_to_bus(vadr));
                *(re++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
                *(re++)=cpu_to_le32(virt_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);
       
        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)
{
        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)
                cprintf("bttv%d: prisc1: ro=%08lx re=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)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(virt_to_bus(vadr));
                 vadr+=bl;
                 if((rcmd&(15<<28))==BT848_RISC_WRITE123)
                 {
                        *((*rp)++)=cpu_to_le32(virt_to_bus(cbadr));
                        cbadr+=blcb;
                        *((*rp)++)=cpu_to_le32(virt_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)
                cprintf("bttv%d: prisc2: ro=%08lx re=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re));

        return 0;
}
 
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)
{
        unsigned long line;
        unsigned long bpl;  /* bytes per line */
        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)
                cprintf("bttv%d: vrisc1: ro=%08lx re=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re));
       
        inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
        bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2;
       
        *(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;

                        *((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
                                BT848_RISC_EOL|bpl);
                        *((*rp)++)=cpu_to_le32(virt_to_bus(vadr));
                        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);

        if (bttv_debug > 1)
                cprintf("bttv%d: vrisc2: ro=%08lx re=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)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)
{
        unsigned char lmask, rmask, *p;
        int W, l, r;
        int i;

        if (bttv_debug > 1)
                cprintf("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;
        }
               

}

static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr)
{
        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)
                cprintf("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=(unsigned int *)btv->risc_scr_odd;
        re=(unsigned int *)btv->risc_scr_even;

        if (bttv_debug)
                cprintf("bttv%d: clip: ro=%08lx re=%08lx\n",
                       btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re));

        if ((clipmap=vmalloc_32(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);
       
        /* 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 - (unsigned int *)btv->risc_scr_odd>(RISCMEM_LEN>>3) - 16)
                                        outofmem++;
                                if (re - (unsigned int *)btv->risc_scr_even>(RISCMEM_LEN>>3) - 16)
                                        outofmem++;
                        }
                        x++;
                        if (0 == (x&7))
                                clipline++;
                }
                if ((!inter)||(line&1))
                        adr+=bpl;
        }

        vfree_32(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);

        if (bttv_debug > 1)
                cprintf("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
                       btv->nr,btv->picture.palette,width,height,bpl,bpp);
}

/*
 *      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)
{
        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);
}


static void bt848_set_geo(struct bttv *btv)
{
        u16 ewidth, eheight, owidth, oheight;
        u16 format, bswap;
        struct tvnorm *tvn;

        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);

        btv->pll.pll_ofreq = tvn->Fsc;
        if (!in_irq) 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;
        } 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;
        }

        /* program odd + even fields */
        bt848_set_eogeo(btv, tvn, 1, owidth, oheight);
        bt848_set_eogeo(btv, tvn, 0, ewidth, eheight);

        btwrite(format, BT848_COLOR_FMT);
        btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
}


static int bpp2fmt[4] = {
        BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16,
        BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32
};

static void bt848_set_winsize(struct bttv *btv)
{
        unsigned short format;

        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);
}

/*
 *      Grab into virtual memory.
 */

static int vgrab(struct bttv *btv, struct video_mmap *mp)
{
        unsigned int *ro, *re;
        unsigned int *vbuf;

        if(btv->fbuffer==NULL)
        {
                if(fbuffer_alloc(btv))
                        return -ENOBUFS;
        }

        if(mp->frame >= gbuffers || mp->frame < 0)
                return -EINVAL;
               
        if(mp->height < 32 || mp->width < 48)
                return -EINVAL;
        if (mp->format >= PALETTEFMT_MAX)
                return -EINVAL;

        if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2
            > gbufsize)
                return -EINVAL;
        if (-1 == palette2fmt[mp->format])
                return -EINVAL;

        /*
         *      Ok load up the BT848
         */

         
        vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame);
        ro=(unsigned int *)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);
        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((unsigned long)ro);
        btv->gbuf[mp->frame].re      = virt_to_bus((unsigned long)re);

#if 1
        if (mp->height <= tvnorms[btv->win.norm].sheight/2 &&
            mp->format != VIDEO_PALETTE_RAW)
                btv->gbuf[mp->frame].ro = 0;
#endif

        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;

        btor(3, BT848_CAP_CTL);
        btor(3, BT848_GPIO_DMA_CTL);
        return 0;
}

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);
}

/*
 * 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;

        /* 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;

}

static void bt848_restart(struct bttv *btv)
{
        if (bttv_verbose)
                printk(KERN_INFO "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((unsigned long)btv->risc_jmp+8),
                BT848_RISC_STRT_ADD);

        /* enforce pll reprogramming */
        btv->pll.pll_current = 0;
        set_pll(btv);

        btv->errors = 0;
        btv->needs_restart = 0;
        bt848_set_geo(btv);
        bt848_set_risc_jmps(btv,-1);
}

/*
 *      Open a bttv card. Right now the flags stuff is just playing
 */


int bttv_open(struct bttv *btv)
{
        int i,ret;

        ret = -EBUSY;
        if (bttv_debug)
                cprintf("bttv%d: open called\n",btv->nr);

        btv->fbuffer=(unsigned char *) vmalloc_32(gbuffers*gbufsize);
        ret = -ENOMEM;

        btv->gq_in = 0;
        btv->gq_out = 0;
        btv->gq_grab = -1;
        for (i = 0; i < gbuffers; i++)
                btv->gbuf[i].stat = GBUFFER_UNUSED;

        burst(0);
        set_pll(btv);
        btv->user++;
        return 0;

}

void bttv_close(struct bttv *btv)
{
        unsigned long tmp;
        int need_wait;

        //bt848_dma(btv,0);

        btv->user--;
        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);

        handler_remove(btv->dev->irq);
       
        /*
         *      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.
         */


        tmp = btread(BT848_I2C);        /* This fixes the PCI posting delay */
       
        if(btv->fbuffer)
                vfree_32((void *) btv->fbuffer);
        btv->fbuffer=0;
}


/***********************************/
/* ioctls and supporting functions */
/***********************************/

static inline void bt848_bright(struct bttv *btv, uint bright)
{
        btwrite(bright&0xff, BT848_BRIGHT);
}

static inline void bt848_hue(struct bttv *btv, uint hue)
{
        btwrite(hue&0xff, BT848_HUE);
}

static inline void bt848_contrast(struct bttv *btv, uint cont)
{
        unsigned int conthi;

        conthi=(cont>>6)&4;
        btwrite(cont&0xff, BT848_CONTRAST_LO);
        btaor(conthi, ~4, BT848_E_CONTROL);
        btaor(conthi, ~4, BT848_O_CONTROL);
}

static inline void bt848_sat_u(struct bttv *btv, unsigned long data)
{
        u32 datahi;

        datahi=(data>>7)&2;
        btwrite(data&0xff, BT848_SAT_U_LO);
        btaor(datahi, ~2, BT848_E_CONTROL);
        btaor(datahi, ~2, BT848_O_CONTROL);
}

static inline void bt848_sat_v(struct bttv *btv, unsigned long data)
{
        u32 datahi;

        datahi=(data>>8)&1;
        btwrite(data&0xff, BT848_SAT_V_LO);
        btaor(datahi, ~1, BT848_E_CONTROL);
        btaor(datahi, ~1, BT848_O_CONTROL);
}

/*
 *      ioctl routine
 */


int bttv_ioctl(struct bttv *btv, unsigned int cmd, void *arg)
{
        int i;

        if (bttv_debug > 1)
                cprintf("bttv%d: ioctl 0x%x\n",btv->nr,cmd);

        switch (cmd) {
        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) |
                        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;
        }
        case VIDIOCGCHAN:
        {
                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;
                return 0;
        }
        /*
         *      Each channel has 1 tuner
         */

        case VIDIOCSCHAN:
        {
                struct video_channel v;
                if(copy_from_user(&v, arg,sizeof(v)))
                        return -EFAULT;
               
                if (v.channel>bttv_tvcards[btv->type].video_inputs)
                        return -EINVAL;
                if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms)))
                        return -EOPNOTSUPP;

                bttv_call_i2c_clients(btv,cmd,&v);
                bt848_muxsel(btv, v.channel);
                btv->channel=v.channel;
                if (btv->win.norm != v.norm) {
                        if(btv->type== BTTV_VOODOOTV_FM)
                                bttv_tda9880_setnorm(btv,v.norm);
                        btv->win.norm = v.norm;
                        make_vbitab(btv);
                        bt848_set_winsize(btv);
                }
                return 0;
        }
        case VIDIOCGTUNER:
        {
                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 */
                        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;
                return 0;
        }
        /* We have but one tuner */
        case VIDIOCSTUNER:
        {
                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;
                        set_pll(btv);
                        make_vbitab(btv);
                        bt848_set_winsize(btv);
                }
                return 0;
        }
        case VIDIOCGPICT:
        {
                struct video_picture p=btv->picture;
                if(copy_to_user(arg, &p, sizeof(p)))
                        return -EFAULT;
                return 0;
        }
        case VIDIOCSPICT:
        {
                struct video_picture p;
                if (copy_from_user(&p, arg,sizeof(p)))
                        return -EFAULT;
                if (p.palette > PALETTEFMT_MAX)
                        return -EINVAL;
                if (-1 == palette2fmt[p.palette])
                        return -EINVAL;
                /* 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;
                return 0;
        }
        case VIDIOCSWIN:
        {
                struct video_window vw;
                struct video_clip *vcp = NULL;
                       
                if(copy_from_user(&vw,arg,sizeof(vw)))
                        return -EFAULT;

                if(vw.flags || vw.width < 16 || vw.height < 16)
                {
                        btv->scr_on = 0;
                        bt848_set_risc_jmps(btv,-1);
                        return -EINVAL;
                }
                if (btv->win.bpp < 4)
                {       /* adjust and align writes */
                        vw.x = (vw.x + 3) & ~3;
                        vw.width &= ~3;
                }
                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;
               
                bt848_set_risc_jmps(btv,0);
                bt848_set_winsize(btv);

                /*
                 *      Do any clips.
                 */

                if(vw.clipcount<0) {
                        if((vcp=vmalloc_32(VIDEO_CLIPMAP_SIZE))==NULL) {
                                return -ENOMEM;
                        }
                        if(copy_from_user(vcp, vw.clips,
                                          VIDEO_CLIPMAP_SIZE)) {
                                vfree_32(vcp);
                                return -EFAULT;
                        }
                } else if (vw.clipcount > 2048) {
                        return -EINVAL;
                } else if (vw.clipcount) {
                        if((vcp=vmalloc_32(sizeof(struct video_clip)*
                                        (vw.clipcount))) == NULL) {
                                return -ENOMEM;
                        }
                        if(copy_from_user(vcp,vw.clips,
                                          sizeof(struct video_clip)*
                                          vw.clipcount)) {
                                vfree_32(vcp);
                                return -EFAULT;
                        }
                }
                make_clip_tab(btv, vcp, vw.clipcount);
                if (vw.clipcount != 0)
                        vfree_32(vcp);
                bt848_set_risc_jmps(btv,-1);
                return 0;
        }
        case VIDIOCGWIN:
        {
                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;
                return 0;
        }
        case VIDIOCCAPTURE:
        {
                int v;
                if(copy_from_user(&v, arg,sizeof(v)))
                        return -EFAULT;
                if(btv->win.vidadr == 0)
                        return -EINVAL;
                if (btv->win.width==0 || btv->win.height==0)
                        return -EINVAL;
                if (1 == no_overlay)
                        return -EIO;
                if (v == 1 && btv->win.vidadr != 0)
                        btv->scr_on = 1;
                if (v == 0)
                        btv->scr_on = 0;
                bt848_set_risc_jmps(btv,-1);
                return 0;
        }
        case VIDIOCGFBUF:
        {
                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;
                return 0;
                       
        }
        case VIDIOCSFBUF:
        {
                struct video_buffer v;
                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)
                        return -EINVAL;
                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;
                }
#endif
       
                if (bttv_debug)
                        cprintf("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
                               v.base, v.width,v.height, btv->win.bpp, btv->win.bpl);
                bt848_set_winsize(btv);
                return 0;              
        }
        case VIDIOCKEY:
        {
                /* Will be handled higher up .. */
                return 0;
        }
        case VIDIOCGFREQ:
        {
                unsigned long v=btv->win.freq;
                if(copy_to_user(arg,&v,sizeof(v)))
                        return -EFAULT;
                return 0;
        }
        case VIDIOCSFREQ:
        {
                unsigned long v;
                if(copy_from_user(&v, arg, sizeof(v)))
                        return -EFAULT;
                btv->win.freq=v;
                bttv_call_i2c_clients(btv,cmd,&v);
                return 0;
        }
       
        case VIDIOCSYNC:
        {
                return (btv->gbuf[0].stat == GBUFFER_DONE);
               
        }

        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;
                btv->pll.pll_ifreq = p.pll_ifreq;
                btv->pll.pll_ofreq = p.pll_ofreq;
                btv->pll.pll_crystal = p.pll_crystal;
                break;
        }

        case VIDIOCMCAPTURE:
        {
                struct video_mmap vm;
                int ret;
                if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
                        return -EFAULT;
                ret = vgrab(btv, &vm);
                return ret;
        }
               
        case VIDIOCGMBUF:
        {
                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;
        }
               
        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;
                return 0;
        }
               
        case BTTV_BURST_ON:
        {
                burst(1);
                return 0;
        }

        case BTTV_BURST_OFF:
        {
                burst(0);
                return 0;
        }

        case BTTV_VERSION:
        {
                return 0;
        }
                       
        case BTTV_PICNR:
        {
                /* return picture;*/
                return  0;
        }

        default:
                return 0;
        }
        return 0;
}

static void bt848_set_risc_jmps(struct bttv *btv, int flags)
{
        if (-1 == flags) {
                /* defaults */
                flags = 0;
                if (btv->scr_on)
                        flags |= 0x03;
                if (btv->vbi_on)
                        flags |= 0x0c;
#if 0
                /* Hmm ... */
                if ((0 != btv->risc_cap_even) ||
                    (0 != btv->risc_cap_odd))
                        flags |= 0x0c;
#endif
        }

        if (bttv_debug > 1)
                cprintf("bttv%d: set_risc_jmp %08lx:",
                       btv->nr,virt_to_bus((unsigned long)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)
                        cprintf(" ev=%08lx",virt_to_bus((unsigned long)btv->vbi_odd));
                btv->risc_jmp[3]=cpu_to_le32(virt_to_bus((unsigned long)btv->vbi_odd));
        } else {
                if (bttv_debug > 1)
                        cprintf(" -----------");
                btv->risc_jmp[3]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+16));
        }

        /* 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)
                        cprintf(" e%d=%08lx",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)
                        cprintf(" eo=%08lx",virt_to_bus((unsigned long)btv->risc_scr_odd));
                btv->risc_jmp[5]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_scr_odd));
        } else {
                if (bttv_debug > 1)
                        cprintf(" -----------");
                btv->risc_jmp[5]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+24));
        }


        /* 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);

        /* Jump to even vbi sub */
        btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP);
        if (flags&4) {
                if (bttv_debug > 1)
                        cprintf(" ov=%08lx",virt_to_bus((unsigned long)btv->vbi_even));
                btv->risc_jmp[9]=cpu_to_le32(virt_to_bus((unsigned long)btv->vbi_even));
        } else {
                if (bttv_debug > 1)
                        cprintf(" -----------");
                btv->risc_jmp[9]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+40));
        }

        /* 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)
                        cprintf(" o%d=%08lx",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)
                        cprintf(" oo=%08lx",virt_to_bus((unsigned long)btv->risc_scr_even));
                btv->risc_jmp[11]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_scr_even));
        } else {
                if (bttv_debug > 1)
                        cprintf(" -----------");
                btv->risc_jmp[11]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+48));
        }

        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((unsigned long)btv->risc_jmp));

        /* enable cpaturing and DMA */
        if (bttv_debug > 1)
                cprintf(" 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);
}

static int init_bt848(struct bttv *btv)
{
        int j,val;

        btv->user=0;

        /* bringup the gpio internals (quick! before anyone uses it) */
        init_gpio_adapter(&(btv->gpio_adap));
       
        /* 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");


        /* 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;

        btv->errors=0;
        btv->needs_restart=0;
        btv->has_radio=radio[btv->nr];

        if (!(btv->risc_scr_odd= (unsigned long *) vmalloc_32(RISCMEM_LEN/2)))
                return -1;
        if (!(btv->risc_scr_even=(unsigned long *) vmalloc_32(RISCMEM_LEN/2)))
                return -1;
        if (!(btv->risc_jmp =(unsigned long *) vmalloc_32(2048)))
                return -1;
        btv->vbi_odd=btv->risc_jmp+64;
        btv->vbi_even=btv->vbi_odd+1024;
        btv->bus_vbi_odd=virt_to_bus((unsigned long)btv->risc_jmp+48);
        btv->bus_vbi_even=virt_to_bus((unsigned long)btv->risc_jmp+24);

        btwrite(virt_to_bus((unsigned long)btv->risc_jmp+8), BT848_RISC_STRT_ADD);
        btv->vbibuf=(unsigned char *) vmalloc_32(VBIBUF_SIZE);
        if (!btv->vbibuf)
                return -1;
        if (!(btv->gbuf = vmalloc_32(sizeof(struct bttv_gbuf)*gbuffers)))
                return -1;
        for (j = 0; j < gbuffers; j++) {
                if (!(btv->gbuf[j].risc = vmalloc_32(16384)))
                        return -1;
        }
       
        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");

        btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO,
                BT848_IFORM);

        btwrite(0xd8, BT848_CONTRAST_LO);
        bt848_bright(btv, 0x10);

        btwrite(0x20, BT848_E_VSCALE_HI);
        btwrite(0x20, BT848_O_VSCALE_HI);
        btwrite(BT848_ADC_RESERVED | (adc_crush ? BT848_ADC_CRUSH : 0),
                BT848_ADC);

        if (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);
        }

        btv->picture.colour=254<<7;
        btv->picture.brightness=128<<8;
        btv->picture.hue=128<<8;
        btv->picture.contrast=0xd8<<7;

        val = chroma_agc ? BT848_SCLOOP_CAGC : 0;
        btwrite(val, BT848_E_SCLOOP);
        btwrite(val, 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);
        bt848_set_risc_jmps(btv,-1);

        /* needs to be done before i2c is registered */
        bttv_init_card1(btv);

        /* register i2c */
        btv->tuner_type=-1;
        init_bttv_i2c(btv);

        /* some card-specific stuff (needs working i2c) */
        bttv_init_card2(btv);

        return 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" };

static void bttv_irq(void)
{
        u32 stat,astat;
        u32 dstat;
        int count;
        struct bttv *btv = btvirq;

        in_irq = 1;
        count=0;
        while (1)
        {
                /* get/clear interrupt status bits */
                stat=btread(BT848_INT_STAT);
                astat=stat&btread(BT848_INT_MASK);
                if (!astat) {
                        in_irq = 0;
                        return;
                }
                btwrite(stat,BT848_INT_STAT);

                /* get device status bits */
                dstat=btread(BT848_DSTATUS);

                if (irq_debug) {
                        int i;
                        cprintf("bttv%d: irq loop=%d risc=%lx, bits:",
                               btv->nr, count, stat>>28);
                        for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
                                if (stat & (1 << i))
                                        cprintf(" %s",irq_name[i]);
                                if (astat & (1 << i))
                                        cprintf(" *");
                        }
                        if (stat & BT848_INT_HLOCK)
                                cprintf("   HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
                                       ? "yes" : "no");
                        if (stat & BT848_INT_VPRES)
                                cprintf("   PRES => %s", (dstat & BT848_DSTATUS_PRES)
                                       ? "yes" : "no");
                        if (stat & BT848_INT_FMTCHG)
                                cprintf("   NUML => %s", (dstat & BT848_DSTATUS_PRES)
                                       ? "625" : "525");
                        cprintf("\n");
                }

                if (astat&BT848_INT_VSYNC)
                        btv->field++;

                if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) {
                        if (bttv_verbose)
                                cprintf("bttv%d: irq:%s%s risc_count=%08lx\n",
                                       btv->nr,
                                       (astat&BT848_INT_SCERR) ? " SCERR" : "",
                                       (astat&BT848_INT_OCERR) ? " OCERR" : "",
                                       btread(BT848_RISC_COUNT));
                        btv->errors++;
                        if (btv->errors < BTTV_ERRORS) {
                                btand(~15, BT848_GPIO_DMA_CTL);
                                btwrite(virt_to_bus((unsigned long)(btv->risc_jmp+8)),
                                        BT848_RISC_STRT_ADD);
                                bt848_set_geo(btv);
                                bt848_set_risc_jmps(btv,-1);
                        } else {
                                if (bttv_verbose)
                                        cprintf("bttv%d: aiee: error loops\n",btv->nr);
                                bt848_offline(btv);
                        }
                }
                if (astat&BT848_INT_RISCI)
                {
                        if (bttv_debug > 1)
                                cprintf("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]))++;
                        }

                        /* captured full frame */
                        if (stat&(2<<28) && btv->gq_grab != -1)
                        {
                                btv->last_field=btv->field;
                                if (bttv_debug)
                                        cprintf("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab);
                                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)
                                                cprintf("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);
                                        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);
                                        btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA,
                                                BT848_COLOR_CTL);
                                }
                                break;
                        }
                        if (stat&(8<<28) && btv->gq_start)
                        {
                                btv->gq_start = 0;
                                btv->gq_grab = btv->gqueue[btv->gq_out++];
                                btv->gq_out  = btv->gq_out % MAX_GBUFFERS;
                                if (bttv_debug)
                                        cprintf("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);
                                btwrite(BT848_COLOR_CTL_GAMMA,
                                        BT848_COLOR_CTL);
                        }
                }

                count++;
                if (count > 20) {
                        btwrite(0, BT848_INT_MASK);
                        cprintf("bttv%d: IRQ lockup, cleared int mask\n", btv->nr);
                        bt848_offline(btv);
                }
        }

        in_irq = 0;
       
}

int bttv_probe(struct bttv *btv)
{
        unsigned char lat;
       
        if (bttv_num == BTTV_MAX)
                return -ENOMEM;
        printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);

        btvirq = btv;
        btv->nr = bttv_num;
        //because not all compilers allow anonymous unions duplication is needed
        btv->gpio_adap.bt848_mem = btv->bt848_mem = NULL;
        btv->vbibuf=NULL;
        btv->risc_jmp=NULL;
        btv->vbi_odd=NULL;
        btv->vbi_even=NULL;
        btv->vbip=VBIBUF_SIZE;
        btv->shutdown=0;
       
        btv->id=848;
        btv->bt848_adr=pci_resource_start(btv->dev,0);
        if (btv->id >= 878)
                btv->i2c_command = 0x83;                  
        else
                btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);

        if (-1 != latency) {
                printk(KERN_INFO "bttv%d: setting pci latency timer to %d\n",
                       bttv_num,latency);
                pci_write_config_byte(btv->dev, PCI_LATENCY_TIMER, latency);
        }
        pci_read_config_byte(btv->dev, PCI_CLASS_REVISION, &btv->revision);
        pci_read_config_byte(btv->dev, PCI_LATENCY_TIMER, &lat);
        printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %2x:%2x\n", bttv_num,btv->id, btv->revision, btv->dev->bus->number, btv->dev->devfn);
        printk(KERN_INFO "irq: %d, latency: %d, mmio: 0x%lx\n",
               btv->dev->irq, lat, btv->bt848_adr);
       
        btv->bt848_mem = (void *)btv->bt848_adr;
        btv->gpio_adap.bt848_mem = btv->bt848_mem;

        btv->pll.pll_crystal = 0;
       
        bttv_idcard(btv);

        /* clear interrupt mask */
        btwrite(0, BT848_INT_MASK);

        handler_set(btv->dev->irq, (void (*)(int)) bttv_irq, NIL);
       
        bttv_handle_chipset(btv);
       
        init_bt848(btv);
       
        bttv_num++;

        return 0;

}

extern void i2c_init_all(void);

int bttv_start(struct bttv *btv)
{

        BYTE dv, bus;
        DWORD dw;

        printk(KERN_INFO "Initializing PCI BUS...\n");
   
        btv->dev = vmalloc_32(sizeof(struct pci_dev));

        /* Scan the devices connected to the PCI bus */
        if (pci_init() == 1) {
                clear();
                pci_show();
        } else {
                return -1;
        }

        bttv_num = 0;

        if (gbuffers < 2 || gbuffers > MAX_GBUFFERS)
                gbuffers = 2;
        if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF)
                gbufsize = BTTV_MAX_FBUF;
        if (bttv_verbose)
                printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n",
                       gbuffers,gbufsize/1024,gbuffers*gbufsize/1024);

        if(pci_present()) {
       
            pci_class(PCI_CLASS_MULTIMEDIA_VIDEO<<8,0,&bus,&dv);
         
            btv->dev->bus->number = bus;
            btv->dev->devfn = dv;

            pcibios_read_config_dword(bus, dv, PCI_BASE_ADDRESS_0, (DWORD *)&(btv->dev->base_address[0]));
            pcibios_read_config_byte(bus, dv, PCI_INTERRUPT_LINE, (BYTE *)&(btv->dev->irq));

            pcibios_read_config_dword(bus,dv,PCI_COMMAND,&dw);
            dw |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
            pcibios_write_config_dword(bus,dv,PCI_COMMAND,dw);
           
        } else return -1;

        i2c_init_all();

        bttv_check_chipset();

        bttv_probe(btv);
       
        bttv_open(btv);

        return 0;

}