/* 
    bttv - Bt848 frame grabber driver

    Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.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.
    
    Modified to put the RISC code writer in the kernel and to fit a
    common (and I hope safe) kernel interface. When we have an X extension
    all will now be really sweet.
*/

#include <linux/module.h>
#include <linux/bios32.h>
#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/pci.h>
#include <linux/signal.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h>
#include <linux/videodev.h>

#include <linux/version.h>
#include <asm/uaccess.h>

#include "bttv.h"
#include "tuner.h"

#define DEBUG(x)		/* Debug driver */	
#define IDEBUG(x)		/* Debug interrupt handler */

static unsigned int remap=0;
static unsigned int vidmem=0;
static unsigned int tuner=0;	/* Default tuner */
MODULE_PARM(tuner,"i");

static int find_vga(void);
static void bt848_set_risc_jmps(struct bttv *btv);

/* Anybody who uses more than four? */
#define BTTV_MAX 4

static int bttv_num;
static struct bttv bttvs[BTTV_MAX];

#define I2C_TIMING (0x7<<4)
#define I2C_COMMAND (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA)

#define AUDIO_MUTE_DELAY 10000
#define FREQ_CHANGE_DELAY 20000
#define EEPROM_WRITE_DELAY 20000


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

/* convert virtual user memory address to physical address */
/* (virt_to_phys only works for kmalloced kernel memory) */

static inline ulong uvirt_to_phys(ulong adr)
{
	pgd_t *pgd;
	pmd_t *pmd;
	pte_t *ptep, pte;
  
/*  printk("adr: 0x%08x\n",adr);*/
	pgd = pgd_offset(current->mm, adr);
/*  printk("pgd: 0x%08x\n",pgd);*/
	if (pgd_none(*pgd))
		return 0;
	pmd = pmd_offset(pgd, adr);
/*  printk("pmd: 0x%08x\n",pmd); */
	if (pmd_none(*pmd))
		return 0;
	ptep = pte_offset(pmd, adr&(~PGDIR_MASK));
	pte = *ptep;
	if(pte_present(pte))
		return (pte_page(pte)|(adr&(PAGE_SIZE-1)));
	return 0;
}

/* convert virtual kernel memory address to physical address */
/* (virt_to_phys only works for kmalloced kernel memory) */

static inline ulong kvirt_to_phys(ulong adr) 
{
	return uvirt_to_phys(VMALLOC_VMADDR(adr));
}

static inline ulong kvirt_to_bus(ulong adr) 
{
	return virt_to_bus(phys_to_virt(kvirt_to_phys(adr)));
}


/*****************/
/* I2C functions */
/*****************/

static int I2CRead(struct bttv *btv, int addr)
{
	u32 i;
	u32 stat;
  
	/* clear status bit ; BT848_INT_RACK is ro */
	btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
  
	btwrite(((addr & 0xff) << 24) | I2C_COMMAND, BT848_I2C);

	/*
	 * Timeout for I2CRead is 1 second (this should be enough, really!)
	 */
	for (i=1000; i; i--)
	{
		stat=btread(BT848_INT_STAT);
		if (stat & BT848_INT_I2CDONE)
			break;
		udelay(1000); /* 1ms, as I2C is 1kHz (?) */
	}
  
	if (!i) {
		printk(KERN_DEBUG "bttv: I2CRead timeout\n");
		return -1;
	}
	if (!(stat & BT848_INT_RACK))
		return -2;
  
	i=(btread(BT848_I2C)>>8)&0xff;
	return i;
}

/* set both to write both bytes, reset it to write only b1 */

static int I2CWrite(struct bttv *btv, unchar addr, unchar b1,
		    unchar b2, int both)
{
	u32 i;
	u32 data;
	u32 stat;
  
	/* clear status bit; BT848_INT_RACK is ro */
	btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
  
	data=((addr & 0xff) << 24) | ((b1 & 0xff) << 16) | I2C_COMMAND;
	if (both)
	{
		data|=((b2 & 0xff) << 8);
		data|=BT848_I2C_W3B;
	}
  
	btwrite(data, BT848_I2C);

	for (i=1000; i; i--)
	{
		stat=btread(BT848_INT_STAT);
		if (stat & BT848_INT_I2CDONE)
			break;
		udelay(1000);
	}
  
	if (!i) {
		printk(KERN_DEBUG "bttv: I2CWrite timeout\n");
		return -1;
	}
	if (!(stat & BT848_INT_RACK))
		return -2;
  
	return 0;
}

static void readee(struct bttv *btv, unchar *eedata)
{
	int i, k;
  
	if (I2CWrite(btv, 0xa0, 0, -1, 0)<0)
	{
		printk(KERN_WARNING "bttv: readee error\n");
		return;
	}

	for (i=0; i<256; i++)
	{
		k=I2CRead(btv, 0xa1);
		if (k<0)
		{
			printk(KERN_WARNING "bttv: readee error\n");
			break;
		}
		eedata[i]=k;
	}
}

static void writeee(struct bttv *btv, unchar *eedata)
{
	int i;
  
	for (i=0; i<256; i++)
	{
		if (I2CWrite(btv, 0xa0, i, eedata[i], 1)<0)
		{
			printk(KERN_WARNING "bttv: writeee error (%d)\n", i);
			break;
		}
		udelay(EEPROM_WRITE_DELAY);
	}
}

/*
 *	Tuner, internal, external and mute 
 */
 
static unchar audiomuxs[][4] = 
{
	{ 0x00, 0x00, 0x00, 0x00}, /* unknown */
	{ 0x02, 0x00, 0x00, 0x0a}, /* MIRO */
	{ 0x00, 0x02, 0x03, 0x04}, /* Hauppauge */
	{ 0x04, 0x02, 0x03, 0x01}, /* STB */
	{ 0x01, 0x02, 0x03, 0x04}, /* Intel??? */
	{ 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000??? */
};

static void audio(struct bttv *btv, int mode)
{
	btwrite(0x0f, BT848_GPIO_OUT_EN);
	btwrite(0x00, BT848_GPIO_REG_INP);

	switch (mode)
	{
		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 ((btv->audio&AUDIO_MUTE) || !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
		mode=AUDIO_OFF;
	btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA);
}


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


static void bt848_cap(struct bttv *btv, uint state)
{
	if (state) 
	{
		btv->cap|=3;
		bt848_set_risc_jmps(btv);
	}
	else
	{
		btv->cap&=~3;
		bt848_set_risc_jmps(btv);
	}
}

static void bt848_muxsel(struct bttv *btv, uint input)
{
	input&=3;

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

	if (input==3) 
	{
		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);
	}
	if (input==2)
		input=3;
	btaor(((input+2)&3)<<5, ~(3<<5), BT848_IFORM);
	audio(btv, input ? AUDIO_EXTERN : AUDIO_TUNER);
}


#define VBIBUF_SIZE 65536

static void make_vbitab(struct bttv *btv)
{
	int i;
	dword *po=(dword *) btv->vbi_odd;
	dword *pe=(dword *) btv->vbi_even;
  
	DEBUG(printk(KERN_DEBUG "vbiodd: 0x%08x\n",(int)btv->vbi_odd));
	DEBUG(printk(KERN_DEBUG "vbievn: 0x%08x\n",(int)btv->vbi_even));
	DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po));
	DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe));

	*(po++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(po++)=0;
	for (i=0; i<16; i++) 
	{
		*(po++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL|(13<<20);
		*(po++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048);
	}
	*(po++)=BT848_RISC_JUMP;
	*(po++)=virt_to_bus(btv->risc_jmp+4);

	*(pe++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(pe++)=0;
	for (i=16; i<32; i++) 
	{
		*(pe++)=BT848_RISC_WRITE|2044|BT848_RISC_EOL|BT848_RISC_SOL;
		*(pe++)=kvirt_to_bus((ulong)btv->vbibuf+i*2048);
	}
	*(pe++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16);
	*(pe++)=virt_to_bus(btv->risc_jmp+10);
}

/*
 *	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 void bt848_set_size(struct bttv *btv)
{
	u16 vscale, hscale;
	u32 xsf, sr;
	u16 hdelay, vdelay;
	u16 hactive, vactive;
	u16 inter;
	u8 crop;

	/*
	 *	No window , no try...
	 */
	 
	if (!btv->win.width)
		return;
	if (!btv->win.height)
		return;
    
	inter=(btv->win.interlace&1)^1;

	switch (btv->win.bpp) 
	{
		/*
		 * RGB8 seems to be a 9x5x5 GRB color cube starting at color 16
		 * Why the h... can't they even mention this in the datasheet???
		 */
		case 1: 
			btwrite(BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT);
			btand(~0x10, BT848_CAP_CTL);  /* Dithering looks much better in this mode */
			break;
		case 2: 
			btwrite(BT848_COLOR_FMT_RGB16, BT848_COLOR_FMT);
			btor(0x10, BT848_CAP_CTL);
			break;
		case 3: 
			btwrite(BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT);
			btor(0x10, BT848_CAP_CTL);
			break;
		case 4: 
			btwrite(BT848_COLOR_FMT_RGB32, BT848_COLOR_FMT);
			btor(0x10, BT848_CAP_CTL);
			break;
	}
	
	/*
	 *	Set things up according to the final picture width.
	 */
	 
	hactive=btv->win.width;
	if (hactive < 193) 
	{
		btwrite (2, BT848_E_VTC);
		btwrite (2, BT848_O_VTC);
	}
	else if (hactive < 385) 
	{
		btwrite (1, BT848_E_VTC);
		btwrite (1, BT848_O_VTC);
	}
	else 
	{
		btwrite (0, BT848_E_VTC);
		btwrite (0, BT848_O_VTC);
	}

	/*
	 *	Ok are we doing Never The Same Color or PAL ?
	 */
	 
	if (btv->win.norm==1) 
	{ 
		btv->win.cropwidth=640;
		btv->win.cropheight=480;
		btwrite(0x68, BT848_ADELAY);
		btwrite(0x5d, BT848_BDELAY);
		btaor(BT848_IFORM_NTSC, ~7, BT848_IFORM);
		btaor(BT848_IFORM_XT0, ~0x18, BT848_IFORM);
		xsf = (btv->win.width*365625UL)/300000UL;
		hscale = ((910UL*4096UL)/xsf-4096);
		vdelay=btv->win.cropy+0x16;
		hdelay=((hactive*135)/754+btv->win.cropx)&0x3fe;
	}
	else
	{
		btv->win.cropwidth=768;
		btv->win.cropheight=576;
		if (btv->win.norm==0)
		{ 
			btwrite(0x7f, BT848_ADELAY);
			btwrite(0x72, BT848_BDELAY);
			btaor(BT848_IFORM_PAL_BDGHI, ~BT848_IFORM_NORM, BT848_IFORM);
		}
		else
		{
			btwrite(0x7f, BT848_ADELAY);
			btwrite(0x00, BT848_BDELAY);
			btaor(BT848_IFORM_SECAM, ~BT848_IFORM_NORM, BT848_IFORM);
		}
		btaor(BT848_IFORM_XT1, ~0x18, BT848_IFORM);
		xsf = (btv->win.width*36875UL)/30000UL;
		hscale = ((1135UL*4096UL)/xsf-4096);
		vdelay=btv->win.cropy+0x20;
		hdelay=((hactive*186)/922+btv->win.cropx)&0x3fe;
	}
	sr=((btv->win.cropheight>>inter)*512)/btv->win.height-512;
	vscale=(0x10000UL-sr)&0x1fff;
	vactive=btv->win.cropheight;

#if 0
	printk("bttv: hscale=0x%04x, ",hscale);
	printk("bttv: vscale=0x%04x\n",vscale);

	printk("bttv: hdelay =0x%04x\n",hdelay);
	printk("bttv: hactive=0x%04x\n",hactive);
	printk("bttv: vdelay =0x%04x\n",vdelay);
	printk("bttv: vactive=0x%04x\n",vactive);
#endif

	/*
	 *	Interlace is set elsewhere according to the final image 
	 *	size we desire
	 */
	 
	if (btv->win.interlace) 
	{ 
		btor(BT848_VSCALE_INT, BT848_E_VSCALE_HI);
		btor(BT848_VSCALE_INT, BT848_O_VSCALE_HI);
	}
	else
	{
		btand(~BT848_VSCALE_INT, BT848_E_VSCALE_HI);
		btand(~BT848_VSCALE_INT, BT848_O_VSCALE_HI);
	}

	/*
	 *	Load her up
	 */
	 
	btwrite(hscale>>8, BT848_E_HSCALE_HI);
	btwrite(hscale>>8, BT848_O_HSCALE_HI);
	btwrite(hscale&0xff, BT848_E_HSCALE_LO);
	btwrite(hscale&0xff, BT848_O_HSCALE_LO);

	btwrite((vscale>>8)|(btread(BT848_E_VSCALE_HI)&0xe0), BT848_E_VSCALE_HI);
	btwrite((vscale>>8)|(btread(BT848_O_VSCALE_HI)&0xe0), BT848_O_VSCALE_HI);
	btwrite(vscale&0xff, BT848_E_VSCALE_LO);
	btwrite(vscale&0xff, BT848_O_VSCALE_LO);

	btwrite(hactive&0xff, BT848_E_HACTIVE_LO);
	btwrite(hactive&0xff, BT848_O_HACTIVE_LO);
	btwrite(hdelay&0xff, BT848_E_HDELAY_LO);
	btwrite(hdelay&0xff, BT848_O_HDELAY_LO);

	btwrite(vactive&0xff, BT848_E_VACTIVE_LO);
	btwrite(vactive&0xff, BT848_O_VACTIVE_LO);
	btwrite(vdelay&0xff, BT848_E_VDELAY_LO);
	btwrite(vdelay&0xff, BT848_O_VDELAY_LO);

	crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)|
		((vactive>>4)&0x30)|((vdelay>>2)&0xc0);
	btwrite(crop, BT848_E_CROP);
	btwrite(crop, BT848_O_CROP);
}


/*
 *	The floats in the tuner struct are computed at compile time
 *	by gcc and cast back to integers. Thus we don't violate the
 *	"no float in kernel" rule.
 */
 
static struct tunertype tuners[] = {
	{"Temic PAL", TEMIC, PAL,
		16*140.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 623},
	{"Philips PAL_I", Philips, PAL_I,
		16*140.25,16*463.25,0x00,0x00,0x00,0x00,0x00, 623},
	{"Philips NTSC", Philips, NTSC,
		16*157.25,16*451.25,0xA0,0x90,0x30,0x8e,0xc0, 732},
	{"Philips SECAM", Philips, SECAM,
		16*168.25,16*447.25,0xA3,0x93,0x33,0x8e,0xc0, 623},
	{"NoTuner", NoTuner, NOTUNER,
		0        ,0        ,0x00,0x00,0x00,0x00,0x00, 0},
	{"Philips PAL", Philips, PAL,
		16*168.25,16*447.25,0xA0,0x90,0x30,0x8e,0xc0, 623},
	{"Temic NTSC", TEMIC, NTSC,
		16*157.25,16*463.25,0x02,0x04,0x01,0x8e,0xc2, 732},
	{"TEMIC PAL_I", TEMIC, PAL_I,
		0        ,0        ,0x00,0x00,0x00,0x00,0xc2, 623},
}; 

/*
 *	Set TSA5522 synthesizer frequency in 1/16 Mhz steps
 */

static void set_freq(struct bttv *btv, ushort freq)
{
	u8 config;
	u16 div;
	struct tunertype *tun=&tuners[btv->tuner];
	int oldAudio = btv->audio;

	audio(btv, AUDIO_MUTE);
	udelay(AUDIO_MUTE_DELAY);
	
	if (freq < tun->thresh1) 
		config = tun->VHF_L;
	else if (freq < tun->thresh2) 
		config = tun->VHF_H;
	else
		config = tun->UHF;

	if(freq < tun->thresh1)
		config = tun->VHF_L;
	else if(freq < tun->thresh2)
		config = tun->VHF_H;
	else
		config=tun->UHF;
		
	div=freq+tun->IFPCoff;
	  
	div&=0x7fff;
	
	if (I2CWrite(btv, btv->tuneradr, (div>>8)&0x7f, div&0xff, 1)<0)
		return;
	I2CWrite(btv, btv->tuneradr, tun->config, config, 1);
	if (!(oldAudio & AUDIO_MUTE))
	{
		udelay(FREQ_CHANGE_DELAY);
		audio(btv, AUDIO_UNMUTE);
	}
}

static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
{
	return -EINVAL;
}

static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
{
	struct bttv *btv= (struct bttv *)v;
	int q,todo;

	todo=count;
	while (todo && todo>(q=VBIBUF_SIZE-btv->vbip)) 
	{
		if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
			return -EFAULT;
		todo-=q;
		buf+=q;

/*		btv->vbip=0; */
		cli();
		if (todo && q==VBIBUF_SIZE-btv->vbip) 
		{
			if(nonblock)
			{
				sti();
				if(count==todo)
					return -EWOULDBLOCK;
				return count-todo;
			}
			interruptible_sleep_on(&btv->vbiq);
			sti();
			if(signal_pending(current))
			{
				if(todo==count)
					return -EINTR;
				else
					return count-todo;
			}
		}
	}
	if (todo) 
	{
		if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
			return -EFAULT;
		btv->vbip+=todo;
	}
	return count;
}

/*
 *	Open a bttv card. Right now the flags stuff is just playing
 */
 
static int bttv_open(struct video_device *dev, int flags)
{
	struct bttv *btv = (struct bttv *)dev;
	int users, i;

	switch (flags)
	{
		case 0:
			if (btv->user)
				return -EBUSY;
			btv->user++;
			audio(btv, AUDIO_UNMUTE);
			for (i=users=0; i<bttv_num; i++)
				users+=bttvs[i].user;
			if (users==1)
				find_vga();
			break;
		case 1:
			break;
		case 2:
			btv->vbip=VBIBUF_SIZE;
			btv->cap|=0x0c;
			bt848_set_risc_jmps(btv);
			break;
	}
	MOD_INC_USE_COUNT;
	return 0;   
}

static void bttv_close(struct video_device *dev)
{
	struct bttv *btv=(struct bttv *)dev;
  
	btv->user--;
	audio(btv, AUDIO_MUTE);
	btv->cap&=~3;
#if 0 /* FIXME */	
	if(minor&0x20) 
	{
		btv->cap&=~0x0c;
	}
#endif	
	bt848_set_risc_jmps(btv);

	MOD_DEC_USE_COUNT;  
}

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

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

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

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

extern inline void bt848_sat_u(struct bttv *btv, ulong 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, ulong 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);
}

/*
 *	Cliprect -> risc table.
 *
 *	FIXME: This is generating wrong code when we have some kinds of
 *	rectangle lists. If you generate overlapped rectangles then it
 *	gets a bit confused. Since we add the frame buffer clip rectangles
 *	we need to fix this. Better yet to rewrite this function.
 */
 
static void write_risc_data(struct bttv *btv, struct video_clip *vp, int count)
{
	int i;
	u32 yy, y, x, dx, ox;
	u32 *rmem, *rmem2;
	struct video_clip first, *cur, *cur2, *nx, first2, *prev, *nx2;
	u32 *rp, rpo=0, rpe=0, p, bpsl;
	u32 *rpp;
	u32 mask;
	int interlace;
	int depth;
  
	rmem=(u32 *)btv->risc_odd;
	rmem2=(u32 *)btv->risc_even;
	depth=btv->win.bpp;
  
	/* create y-sorted list  */
	
	first.next=NULL;
	for (i=0; i<count; i++) 
	{
		cur=&first;
		while ((nx=cur->next) && (vp[i].y > cur->next->y))
			cur=nx; 
		cur->next=&(vp[i]);
		vp[i].next=nx;
	}
	first2.next=NULL;
	
	rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem[rpo++]=0;

	rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem2[rpe++]=0;


	/*
	 *	32bit depth frame buffers need extra flags setting
	 */

	if (depth==4)
		mask=BT848_RISC_BYTE3;
	else
		mask=0;
		
	bpsl=btv->win.width*btv->win.bpp;
	p=btv->win.vidadr+btv->win.x*btv->win.bpp+
		btv->win.y*btv->win.bpl;

	interlace=btv->win.interlace;

	/*
	 *	Loop through all lines 
	 */
	 
	for (yy=0; yy<(btv->win.height<<(1^interlace)); yy++) 
	{
		y=yy>>(1^interlace);
		
		/*
		 *	Even or odd frame generation. We have to 
		 *	write the RISC instructions to the right stream.
		 */
		 
		if(!(y&1))
		{
			rp=&rpo;
			rpp=rmem;
		}
		else
		{
			rp=&rpe;
			rpp=rmem2;
		}
		
		
		/*
		 *	first2 is the header of a list of "active" rectangles. We add
		 *	rectangles as we hit their top and remove them as they fall off
		 *	the bottom
		 */
		
		 /* remove rects with y2 > y */
		if ((cur=first2.next)) 
		{
			prev=&first2;
			do 
			{
				if (cur->y+cur->height < y) 
					prev->next=cur->next;
				else
					prev=cur;
			}
			while ((cur=cur->next));
		}

		/*
		 *	Fixme - we have to handle overlapped rectangles
		 *	here, but the overlap might be partial
		 */
		 
		/* add rect to second (x-sorted) list if rect.y == y  */
		if ((cur=first.next)) 
		{
			while ((cur) && (cur->y == y))
			{ 
				first.next=cur->next;
				cur2=&first2;
				while ((nx2=cur2->next) && (cur->x > cur2->next->x)) 
					cur2=nx2; 
				cur2->next=cur;
				cur->next=nx2;
				cur=first.next;
			}
		}
		
		
		/*
		 *	Begin writing the RISC script
		 */
    
		dx=x=0;
		
		/*
		 *	Starting at x position 0 on a new scan line
		 *	write to location p, don't yet write the number
		 *	of pixels for the instruction
		 */
		 
		rpp[(*rp)++]=BT848_RISC_WRITE|BT848_RISC_SOL;
		rpp[(*rp)++]=p;
		
		/*
		 *	For each rectangle we have in the "active" list - sorted left to
		 *	right..
		 */
		 
		for (cur2=first2.next; cur2; cur2=cur2->next) 
		{
			/*
			 *	If we are to the left of the first drawing area
			 */
			 
			if (x+dx < cur2->x) 
			{
				/* Bytes pending ? */
				if (dx) 
				{
					/* For a delta away from the start we need to write a SKIP */
					if (x) 
						rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth);
					else
					/* Rewrite the start of line WRITE to a SKIP */
						rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth);
					/* Move X to the next point (drawing start) */
					x=x+dx;
				}
				/* Ok how far are we from the start of the next rectangle ? */
				dx=cur2->x-x;
				/* dx is now the size of data to write */
				
				/* If this isnt the left edge generate a "write continue" */
				if (x) 
					rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|mask;
				else
					/* Fill in the byte count on the initial WRITE */
					rpp[(*rp)-2]|=(dx*depth)|mask;
				/* Move to the start of the rectangle */
				x=cur2->x;
				/* x is our left dx is byte size of hole */
				dx=cur2->width+1;
			}
			else
			/* Already in a clip zone.. set dx */
				if (x+dx < cur2->x+cur2->width) 
					dx=cur2->x+cur2->width-x+1;
		}
		/* now treat the rest of the line */
		ox=x;
		if (dx) 
		{
			/* Complete the SKIP to eat to the end of the gap */
			if (x) 
				rpp[(*rp)++]=BT848_RISC_SKIP|(dx*depth);
			else
			/* Rewrite to SKIP start to this point */
				rpp[(*rp)-2]|=BT848_RISC_BYTE_ALL|(dx*depth);
			x=x+dx;
		}
		
		/*
		 *	Not at the right hand edge ?
		 */
		 
		if ((dx=btv->win.width-x)!=0) 
		{
			/* Write to edge of display */
			if (x)
				rpp[(*rp)++]=BT848_RISC_WRITEC|(dx*depth)|BT848_RISC_EOL|mask;
			else
			/* Entire frame is a write - patch first order */
				rpp[(*rp)-2]|=(dx*depth)|BT848_RISC_EOL|mask;
		}
		else
		{
			/* End of line if needed */
			if (ox)
				rpp[(*rp)-1]|=BT848_RISC_EOL|mask;
			else 
			{
				/* Skip the line : write a SKIP + start/end of line marks */
				(*rp)--;
				rpp[(*rp)-1]=BT848_RISC_SKIP|(btv->win.width*depth)|
					BT848_RISC_EOL|BT848_RISC_SOL;
			}
		}
		/*
		 *	Move the video render pointer on a line 
		 */
		if (interlace||(y&1))
			p+=btv->win.bpl;
	}
	
	/*
	 *	Attach the interframe jumps
	 */

	rmem[rpo++]=BT848_RISC_JUMP; 
	rmem[rpo++]=btv->bus_vbi_even;

	rmem2[rpe++]=BT848_RISC_JUMP;
	rmem2[rpe++]=btv->bus_vbi_odd;
}

/*
 *	Helper for adding clips.
 */

static void new_risc_clip(struct video_window *vw, struct video_clip *vcp, int x, int y, int w, int h)
{
	vcp[vw->clipcount].x=x;
	vcp[vw->clipcount].y=y;
	vcp[vw->clipcount].width=w;
	vcp[vw->clipcount].height=h;
	vw->clipcount++;
}

/*
 *	ioctl routine
 */
 
static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
	unsigned char eedata[256];
/*	unsigned long data;*/
/*	static ushort creg;*/
	struct bttv *btv=(struct bttv *)dev;
  	static int lastchan=0;
  	
	switch (cmd)
	{	
		case VIDIOCGCAP:
		{
			struct video_capability b;
			strcpy(b.name,btv->video_dev.name);
			b.type = VID_TYPE_CAPTURE|
			 	VID_TYPE_TUNER|
				VID_TYPE_TELETEXT|
				VID_TYPE_OVERLAY|
				VID_TYPE_CLIPPING|
				VID_TYPE_FRAMERAM|
				VID_TYPE_SCALES;
			b.channels = 4; /* tv  , input, svhs */
			b.audios = 4; /* tv, input, svhs */
			b.maxwidth = 768;
			b.maxheight = 576;
			b.minwidth = 32;
			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;
			switch(v.channel)
			{
				case 0:
					strcpy(v.name,"Television");
					v.flags|=VIDEO_VC_TUNER;
					v.type=VIDEO_TYPE_TV;
					v.tuners=1;
					break;
				case 1:
					strcpy(v.name,"Composite1");
					break;
				case 2:
					strcpy(v.name,"Composite2");
					break;
				case 3:
					strcpy(v.name,"SVHS");
					break;
				default:
					return -EINVAL;
			}
			if(copy_to_user(arg,&v,sizeof(v)))
				return -EFAULT;
			return 0;
		}
		/*
		 *	Each channel has 1 tuner
		 */
		case VIDIOCSCHAN:
		{
			int v;
			if(copy_from_user(&v, arg, sizeof(v)))
				return -EFAULT;
			bt848_muxsel(btv, v);
			lastchan=v;
			return 0;
		}
		case VIDIOCGTUNER:
		{
			struct video_tuner v;
			if(copy_from_user(&v,arg,sizeof(v))!=0)
				return -EFAULT;
			if(v.tuner||lastchan)	/* Only tuner 0 */
				return -EINVAL;
			strcpy(v.name, "Television");
			v.rangelow=0;
			v.rangehigh=0xFFFFFFFF;
			v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC;
			v.mode = btv->win.norm;
			if(copy_to_user(arg,&v,sizeof(v)))
				return -EFAULT;
			return 0;
		}
		/* We have but tuner 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 || lastchan)
				return -EINVAL;
			if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC)
				return -EOPNOTSUPP;
			btv->win.norm = v.mode;
			bt848_set_size(btv);
			return 0;
		}
		case VIDIOCGPICT:
		{
			struct video_picture p=btv->picture;
			if(btv->win.bpp==1)
				p.palette=VIDEO_PALETTE_HI240;
			if(btv->win.bpp==2)
				p.palette=VIDEO_PALETTE_RGB565;
			if(btv->win.bpp==3)
				p.palette=VIDEO_PALETTE_RGB24;
			if(btv->win.bpp==4)
				p.palette=VIDEO_PALETTE_RGB32;
			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;
			/* 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;
			int on;
			
			if(copy_from_user(&vw,arg,sizeof(vw)))
				return -EFAULT;
				
			if(vw.flags)
				return -EINVAL;
				
			btv->win.x=vw.x;
			btv->win.y=vw.y;
			btv->win.width=vw.width;
			btv->win.height=vw.height;

			if(btv->win.height>btv->win.cropheight/2)
				btv->win.interlace=1;
			else
				btv->win.interlace=0;

			on=(btv->cap&3)?1:0;
			
			bt848_cap(btv,0);
			bt848_set_size(btv);

			if(vw.clipcount>256)
				return -EDOM;	/* Too many! */

			/*
			 *	Do any clips.
			 */

			vcp=vmalloc(sizeof(struct video_clip)*(vw.clipcount+4));
			if(vcp==NULL)
				return -ENOMEM;
			if(vw.clipcount && copy_from_user(vcp,vw.clips,sizeof(struct video_clip)*vw.clipcount))
				return -EFAULT;
			/*
			 *	Impose display clips
			 */
			if(btv->win.x<0)
				new_risc_clip(&vw, vcp, 0, 0, -btv->win.x, btv->win.height-1);
			if(btv->win.y<0)
				new_risc_clip(&vw, vcp, 0, 0, btv->win.width-1,-btv->win.y);
			if(btv->win.x+btv->win.width> btv->win.swidth)
				new_risc_clip(&vw, vcp, btv->win.swidth-btv->win.x, 0, btv->win.width-1, btv->win.height-1);
			if(btv->win.y+btv->win.height > btv->win.sheight)
				new_risc_clip(&vw, vcp, 0, btv->win.sheight-btv->win.y, btv->win.width-1, btv->win.height-1);
			/*
			 *	Question: Do we need to walk the clip list
			 *	and saw off any clips outside the window 
			 *	frame or will write_risc_tab do the right
			 *	thing ?
			 */
			write_risc_data(btv,vcp, vw.clipcount);
			vfree(vcp);
			if(on)
				bt848_cap(btv,1);
			return 0;
		}
		case VIDIOCGWIN:
		{
			struct video_window vw;
			/* Oh for a COBOL move corresponding .. */
			vw.x=btv->win.x;
			vw.y=btv->win.y;
			vw.width=btv->win.width;
			vw.height=btv->win.height;
			vw.chromakey=0;
			vw.flags=0;
			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 || btv->win.width==0 || btv->win.height==0)
				return -EINVAL;
			if(v==0)
			{
				bt848_cap(btv,0);
			}
			else
			{
				bt848_cap(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.bpp*8;
			v.bytesperline=btv->win.bpl;
			if(copy_to_user(arg, &v,sizeof(v)))
				return -EFAULT;
			return 0;
			
		}
		case VIDIOCSFBUF:
		{
			struct video_buffer v;
			if(!suser())
				return -EPERM;
			if(copy_from_user(&v, arg,sizeof(v)))
				return -EFAULT;
			if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32)
				return -EINVAL;
			btv->win.vidadr=(int)v.base;
			btv->win.sheight=v.height;
			btv->win.swidth=v.width;
			btv->win.bpp=v.depth/8;
			btv->win.bpl=v.bytesperline;
			
			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));
			bt848_set_size(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;
			set_freq(btv, btv->win.freq);
			return 0;
		}
	
		case VIDIOCGAUDIO:
		{
			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");
			if(copy_to_user(arg,&v,sizeof(v)))
				return -EFAULT;
			return 0;
		}
		case VIDIOCSAUDIO:
		{
			struct video_audio v;
			if(copy_from_user(&v,arg, sizeof(v)))
				return -EFAULT;
			if(v.audio!=0)
				return -EINVAL;
			if(v.flags&VIDEO_AUDIO_MUTE)
				audio(btv, AUDIO_MUTE);
			if(!(v.flags&VIDEO_AUDIO_MUTE))
				audio(btv, AUDIO_UNMUTE);
			btv->audio_dev=v;
			return 0;
		}

		case BTTV_WRITEEE:
			if(copy_from_user((void *) eedata, (void *) arg, 256))
				return -EFAULT;
			writeee(btv, eedata);
			break;

		case BTTV_READEE:
			readee(btv, eedata);
			if(copy_to_user((void *) arg, (void *) eedata, 256))
				return -EFAULT;
			break;

		default:
			return -ENOIOCTLCMD;
	}
	return 0;
}

static int bttv_init_done(struct video_device *dev)
{
	return 0;
}

static struct video_device bttv_template=
{
	"UNSET",
	VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
	VID_HARDWARE_BT848,
	bttv_open,
	bttv_close,
	bttv_read,
	bttv_write,
	bttv_ioctl,
	NULL,	/* no mmap yet */
	bttv_init_done,
	NULL,
	0,
	0
};

struct vidbases 
{
	ushort vendor, device;
	char *name;
	uint badr;
};

static struct vidbases vbs[] = {
	{ PCI_VENDOR_ID_TSENG, 0, "TSENG", PCI_BASE_ADDRESS_0},
	{ PCI_VENDOR_ID_MATROX, PCI_DEVICE_ID_MATROX_MIL,
		"Matrox Millennium", PCI_BASE_ADDRESS_1},
	{ PCI_VENDOR_ID_MATROX, 0x051a, "Matrox Mystique", PCI_BASE_ADDRESS_1},
	{ PCI_VENDOR_ID_S3, 0, "S3", PCI_BASE_ADDRESS_0},
	{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_210888GX,
		"ATI MACH64 Winturbo", PCI_BASE_ADDRESS_0},
	{ PCI_VENDOR_ID_CIRRUS, 0, "Cirrus Logic", PCI_BASE_ADDRESS_0},
	{ PCI_VENDOR_ID_N9, PCI_DEVICE_ID_N9_I128, 
		"Number Nine Imagine 128", PCI_BASE_ADDRESS_0},
	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA,
		"DEC DC21030", PCI_BASE_ADDRESS_0},
};


/* DEC TGA offsets stolen from XFree-3.2 */

static uint dec_offsets[4] = {
	0x200000,
	0x804000,
	0,
	0x1004000
};

#define NR_CARDS (sizeof(vbs)/sizeof(struct vidbases))

/* Scan for PCI display adapter
   if more than one card is present the last one is used for now */

static int find_vga(void)
{
	unsigned int devfn, class, vendev;
	ushort vendor, device, badr;
	int found=0, bus=0, i, tga_type;
	unsigned int vidadr=0;


	for (devfn = 0; devfn < 0xff; devfn++) 
	{
		if (PCI_FUNC(devfn) != 0)
			continue;
		pcibios_read_config_dword(bus, devfn, PCI_VENDOR_ID, &vendev);
		if (vendev == 0xffffffff || vendev == 0x00000000) 
			continue;
		pcibios_read_config_word(bus, devfn, PCI_VENDOR_ID, &vendor);
		pcibios_read_config_word(bus, devfn, PCI_DEVICE_ID, &device);
		pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class);
		class = class >> 16;
/*		if (class == PCI_CLASS_DISPLAY_VGA) {*/
		if ((class>>8) == PCI_BASE_CLASS_DISPLAY ||
			/* Number 9 GXE64Pro needs this */
			class == PCI_CLASS_NOT_DEFINED_VGA) 
		{
			badr=0;
			printk(KERN_INFO "bttv: PCI display adapter: ");
			for (i=0; i<NR_CARDS; i++) 
			{
				if (vendor==vbs[i].vendor) 
				{
					if (vbs[i].device) 
						if (vbs[i].device!=device)
							continue;
					printk("%s.\n", vbs[i].name);
					badr=vbs[i].badr;
					break;
				}
			}
			if (!badr) 
			{
				printk(KERN_ERR "bttv: Unknown video memory base address.\n");
				continue;
			}
			pcibios_read_config_dword(bus, devfn, badr, &vidadr);
			if (vidadr & PCI_BASE_ADDRESS_SPACE_IO) 
			{
				printk(KERN_ERR "bttv: Memory seems to be I/O memory.\n");
				printk(KERN_ERR "bttv: Check entry for your card type in bttv.c vidbases struct.\n");
				continue;
			}
			vidadr &= PCI_BASE_ADDRESS_MEM_MASK;
			if (!vidadr) 
			{
				printk(KERN_ERR "bttv: Memory @ 0, must be something wrong!");
				continue;
			}
      
			if (vendor==PCI_VENDOR_ID_DEC)
				if (device==PCI_DEVICE_ID_DEC_TGA) 
			{
				tga_type = (readl((unsigned long)vidadr) >> 12) & 0x0f;
				if (tga_type != 0 && tga_type != 1 && tga_type != 3) 
				{
					printk(KERN_ERR "bttv: TGA type (0x%x) unrecognized!\n", tga_type);
					found--;
				}
				vidadr+=dec_offsets[tga_type];
			}

			DEBUG(printk(KERN_DEBUG "bttv: memory @ 0x%08x, ", vidadr));
			DEBUG(printk(KERN_DEBUG "devfn: 0x%04x.\n", devfn));
			found++;
		}
	}
  
	if (vidmem)
	{
		vidadr=vidmem<<20;
		printk(KERN_INFO "bttv: Video memory override: 0x%08x\n", vidadr);
		found=1;
	}
	for (i=0; i<BTTV_MAX; i++)
		bttvs[i].win.vidadr=vidadr;

	return found;
}

#define  TRITON_PCON	           0x50 
#define  TRITON_BUS_CONCURRENCY   (1<<0)
#define  TRITON_STREAMING	  (1<<1)
#define  TRITON_WRITE_BURST	  (1<<2)
#define  TRITON_PEER_CONCURRENCY  (1<<3)
  
static void handle_chipset(void)
{
	int index;
  
	for (index = 0; index < 8; index++)
	{
		unsigned char bus, devfn;
		unsigned char b, bo;
    
		/* Beware the SiS 85C496 my friend - rev 49 don't work with a bttv */
		
		if (!pcibios_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, index, &bus, &devfn))
		{
			printk(KERN_WARNING "BT848 and SIS 85C496 chipset don't always work together.\n");
		}			

		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441,
			    index, &bus, &devfn)) 
		{
			pcibios_read_config_byte(bus, devfn, 0x53, &b);
			DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
			DEBUG(printk("bufcon=0x%02x\n",b));
		}

		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82441,
			    index, &bus, &devfn)) 
		{
			pcibios_read_config_byte(bus, devfn, 0x53, &b);
			DEBUG(printk(KERN_INFO "bttv: Host bridge: 82441FX Natoma, "));
			DEBUG(printk("bufcon=0x%02x\n",b));
		}

		if (!pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82437,
			    index, &bus, &devfn)) 
		{
			printk(KERN_INFO "bttv: Host bridge 82437FX Triton PIIX\n");
			pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
			bo=b;
			DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));

			/* 430FX (Triton I) freezes with bus concurrency on -> switch it off */
			if(!(b & TRITON_BUS_CONCURRENCY)) 
			{
				printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
				b |= TRITON_BUS_CONCURRENCY;
			}

			/* still freezes on other boards -> switch off even more */
			if(b & TRITON_PEER_CONCURRENCY) 
			{
				printk(KERN_WARNING "bttv: 82437FX: disabling peer concurrency\n");
				b &= ~TRITON_PEER_CONCURRENCY;
			}
			if(!(b & TRITON_STREAMING)) 
			{
				printk(KERN_WARNING "bttv: 82437FX: disabling streaming\n");
				b |=  TRITON_STREAMING;
			}

			if (b!=bo) 
			{
				pcibios_write_config_byte(bus, devfn, TRITON_PCON, b); 
				printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
			}
		}
	}
}

static void init_tda9850(struct bttv *btv)
{
	I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0, 1);
}

/* Figure out card and tuner type */

static void idcard(struct bttv *btv)
{
	int i;

	btwrite(0, BT848_GPIO_OUT_EN);
	DEBUG(printk(KERN_DEBUG "bttv: GPIO: 0x%08x\n", btread(BT848_GPIO_DATA)));

	btv->type=BTTV_MIRO;
	btv->tuner=tuner;
  
	if (I2CRead(btv, I2C_HAUPEE)>=0)
		btv->type=BTTV_HAUPPAUGE;
	else if (I2CRead(btv, I2C_STBEE)>=0)
		btv->type=BTTV_STB;

	for (i=0xc0; i<0xd0; i+=2)
	{
		if (I2CRead(btv, i)>=0) 
		{
			btv->tuneradr=i;
			break;
		}
	}

	btv->dbx = I2CRead(btv, I2C_TDA9850) ? 0 : 1;

	if (btv->dbx)
		init_tda9850(btv);

	/* How do I detect the tuner type for other cards but Miro ??? */
	printk(KERN_INFO "bttv: model: ");
	switch (btv->type) 
	{
		case BTTV_MIRO:
			btv->tuner=((btread(BT848_GPIO_DATA)>>10)-1)&7;
			printk("MIRO");
			strcpy(btv->video_dev.name,"BT848(Miro)");
			break;
		case BTTV_HAUPPAUGE:
			printk("HAUPPAUGE");
			strcpy(btv->video_dev.name,"BT848(Hauppauge)");
			break;
		case BTTV_STB: 
			printk("STB");
			strcpy(btv->video_dev.name,"BT848(STB)");
			break;
		case BTTV_INTEL: 
			printk("Intel");
			strcpy(btv->video_dev.name,"BT848(Intel)");
			break;
		case BTTV_DIAMOND: 
			printk("Diamond");
			strcpy(btv->video_dev.name,"BT848(Diamond)");
			break;
	}
	printk(" (%s @ 0x%02x)\n", tuners[btv->tuner].name, btv->tuneradr);
	audio(btv, AUDIO_MUTE);
}


static void bt848_set_risc_jmps(struct bttv *btv)
{
	int flags=btv->cap;
  
	btv->risc_jmp[0]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRE;
	btv->risc_jmp[1]=0;

	btv->risc_jmp[2]=BT848_RISC_JUMP;
	if (flags&8)
		btv->risc_jmp[3]=virt_to_bus(btv->vbi_odd);
	else
		btv->risc_jmp[3]=virt_to_bus(btv->risc_jmp+4);

	btv->risc_jmp[4]=BT848_RISC_JUMP;
	if (flags&2)
		btv->risc_jmp[5]=virt_to_bus(btv->risc_odd);
	else
		btv->risc_jmp[5]=virt_to_bus(btv->risc_jmp+6);

	btv->risc_jmp[6]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO;
	btv->risc_jmp[7]=0;

	btv->risc_jmp[8]=BT848_RISC_JUMP;
	if (flags&4)
		btv->risc_jmp[9]=virt_to_bus(btv->vbi_even);
	else
		btv->risc_jmp[9]=virt_to_bus(btv->risc_jmp+10);

	btv->risc_jmp[10]=BT848_RISC_JUMP;
	if (flags&1)
		btv->risc_jmp[11]=virt_to_bus(btv->risc_even);
	else
		btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp);

	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)
{
	/* reset the bt848 */
	btwrite(0,BT848_SRESET);
	btv->user=0; 

	DEBUG(printk(KERN_DEBUG "bttv: bt848_mem: 0x%08x\n",(unsigned int) btv->bt848_mem));

	/* 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=768; /* 640 */
	btv->win.height=576; /* 480 */
	btv->win.cropwidth=768; /* 640 */
	btv->win.cropheight=576; /* 480 */
	btv->win.cropx=0;
	btv->win.cropy=0;
	btv->win.bpp=2;
	btv->win.bpl=1024*btv->win.bpp;
	btv->win.swidth=1024;
	btv->win.sheight=768;
	btv->cap=0;

	if (!(btv->risc_odd=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
		return -1;
	if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
		return -1;
	if (!(btv->risc_jmp =(dword *) kmalloc(1024, GFP_KERNEL)))
		return -1;
	btv->vbi_odd=btv->risc_jmp+12;
	btv->vbi_even=btv->vbi_odd+256;
	btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp);
	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=(unchar *) vmalloc(VBIBUF_SIZE);
	if (!btv->vbibuf) 
		return -1;

	bt848_muxsel(btv, 1);
	bt848_set_size(btv);

/*	btwrite(0, BT848_TDEC); */
	btwrite(0x10, BT848_COLOR_CTL);
	btwrite(0x00, BT848_CAP_CTL);

	btwrite(0x0ff, BT848_VBI_PACK_SIZE);
	btwrite(1, BT848_VBI_PACK_DEL);

	btwrite(0xfc, BT848_GPIO_DMA_CTL);
	btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_PAL_BDGHI,
		BT848_IFORM);

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

	btwrite(0x60, BT848_E_VSCALE_HI);
	btwrite(0x60, BT848_O_VSCALE_HI);
	btwrite(/*BT848_ADC_SYNC_T|*/
		BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC);

	btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
	btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
	btwrite(0x00, BT848_E_SCLOOP);
	btwrite(0x00, BT848_O_SCLOOP);

	btwrite(0xffffffUL,BT848_INT_STAT);
/*	  BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|BT848_INT_FDSR|
	  BT848_INT_FTRGT|BT848_INT_FBUS|*/
	btwrite(BT848_INT_ETBF|
		BT848_INT_SCERR|
		BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
		BT848_INT_FMTCHG|BT848_INT_HLOCK,
		BT848_INT_MASK);

/*	make_risctab(btv); */
	make_vbitab(btv);
	bt848_set_risc_jmps(btv);
  
	/*
	 *	Now add the template and register the device unit.
	 */

	memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template));
	idcard(btv);
	
	btv->picture.brightness=0x90<<8;
	btv->picture.contrast = 0x70 << 8;
	btv->picture.colour = 0x70<<8;
	btv->picture.hue = 0x8000;
	
	if(video_register_device(&btv->video_dev)<0)
		return -1;
	return 0;
}


static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
{
	u32 stat,astat;
	u32 dstat;
	int count;
	struct bttv *btv;
 
	btv=(struct bttv *)dev_id;
	count=0;
	while (1) 
	{
		/* get/clear interrupt status bits */
		stat=btread(BT848_INT_STAT);
		astat=stat&btread(BT848_INT_MASK);
		if (!astat)
			return;
		btwrite(astat,BT848_INT_STAT);
		IDEBUG(printk ("bttv: astat %08x\n",astat));
		IDEBUG(printk ("bttv:  stat %08x\n",stat));

		/* get device status bits */
		dstat=btread(BT848_DSTATUS);
    
		if (astat&BT848_INT_FMTCHG) 
		{
			IDEBUG(printk ("bttv: IRQ_FMTCHG\n"));
/*			btv->win.norm&=(dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */
		}
		if (astat&BT848_INT_VPRES) 
		{
			IDEBUG(printk ("bttv: IRQ_VPRES\n"));
		}
		if (astat&BT848_INT_VSYNC) 
		{
			IDEBUG(printk ("bttv: IRQ_VSYNC\n"));
		}
		if (astat&BT848_INT_SCERR) {
			IDEBUG(printk ("bttv: IRQ_SCERR\n"));
			bt848_dma(btv, 0);
			bt848_dma(btv, 1);
			wake_up_interruptible(&btv->vbiq);
			wake_up_interruptible(&btv->capq);
		}
		if (astat&BT848_INT_RISCI) 
		{
			IDEBUG(printk ("bttv: IRQ_RISCI\n"));
			/* printk ("bttv: IRQ_RISCI%d\n",stat>>28); */
			if (stat&(1<<28)) 
			{
				btv->vbip=0;
				wake_up_interruptible(&btv->vbiq);
			}
			if (stat&(2<<28)) 
			{
				bt848_set_risc_jmps(btv);
				wake_up_interruptible(&btv->capq);
				break;
			}
		}
		if (astat&BT848_INT_OCERR) 
		{
			IDEBUG(printk ("bttv: IRQ_OCERR\n"));
		}
		if (astat&BT848_INT_PABORT) 
		{
			IDEBUG(printk ("bttv: IRQ_PABORT\n"));
		}
		if (astat&BT848_INT_RIPERR) 
		{
			IDEBUG(printk ("bttv: IRQ_RIPERR\n"));
		}
		if (astat&BT848_INT_PPERR) 
		{
			IDEBUG(printk ("bttv: IRQ_PPERR\n"));
		}
		if (astat&BT848_INT_FDSR) 
		{
			IDEBUG(printk ("bttv: IRQ_FDSR\n"));
		}
		if (astat&BT848_INT_FTRGT) 
		{
			IDEBUG(printk ("bttv: IRQ_FTRGT\n"));
		}
		if (astat&BT848_INT_FBUS) 
		{
			IDEBUG(printk ("bttv: IRQ_FBUS\n"));
		}
		if (astat&BT848_INT_HLOCK) 
		{
			if (dstat&BT848_DSTATUS_HLOC)
				audio(btv, AUDIO_ON);
			else
				audio(btv, AUDIO_OFF);
		}
    
		if (astat&BT848_INT_I2CDONE) 
		{
		}
    
		count++;
		if (count > 10)
			printk (KERN_WARNING "bttv: irq loop %d\n", count);
		if (count > 20) 
		{
			btwrite(0, BT848_INT_MASK);
			printk(KERN_ERR "bttv: IRQ lockup, cleared int mask\n");
		}
	}
}


/*
 *	Scan for a Bt848 card, request the irq and map the io memory 
 */
 
static int find_bt848(void)
{
	short pci_index;    
	unsigned char command, latency;
	int result;
	unsigned char bus, devfn;
	struct bttv *btv;

	bttv_num=0;

	if (!pcibios_present()) 
	{
		DEBUG(printk(KERN_DEBUG "bttv: PCI-BIOS not present or not accessable!\n"));
		return 0;
	}

	for (pci_index = 0;
		!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
			    pci_index, &bus, &devfn);
		++pci_index) 
	{
		btv=&bttvs[bttv_num];
		btv->bus=bus;
		btv->devfn=devfn;
		btv->bt848_mem=NULL;
		btv->vbibuf=NULL;
		btv->risc_jmp=NULL;
		btv->vbi_odd=NULL;
		btv->vbi_even=NULL;
		btv->vbiq=NULL;
		btv->capq=NULL;
		btv->vbip=VBIBUF_SIZE;

		pcibios_read_config_byte(btv->bus, btv->devfn,
			PCI_INTERRUPT_LINE, &btv->irq);
		pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
			&btv->bt848_adr);
    
		if (remap&&(!bttv_num))
		{ 
			remap<<=20;
			remap&=PCI_BASE_ADDRESS_MEM_MASK;
			printk(KERN_INFO "Remapping to : 0x%08x.\n", remap);
			remap|=btv->bt848_adr&(~PCI_BASE_ADDRESS_MEM_MASK);
			pcibios_write_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
				 remap);
			pcibios_read_config_dword(btv->bus, btv->devfn, PCI_BASE_ADDRESS_0,
				&btv->bt848_adr);
		}					
    
		btv->bt848_adr&=PCI_BASE_ADDRESS_MEM_MASK;
		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_CLASS_REVISION,
			     &btv->revision);
		printk(KERN_INFO "bttv: Brooktree Bt848 (rev %d) ",btv->revision);
		printk("bus: %d, devfn: %d, ",
			btv->bus, btv->devfn);
		printk("irq: %d, ",btv->irq);
		printk("memory: 0x%08x.\n", btv->bt848_adr);
    
		btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);

		result = request_irq(btv->irq, bttv_irq,
			SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
		if (result==-EINVAL) 
		{
			printk(KERN_ERR "bttv: Bad irq number or handler\n");
			return -EINVAL;
		}
		if (result==-EBUSY)
		{
			printk(KERN_ERR "bttv: IRQ %d busy, change your PnP config in BIOS\n",btv->irq);
			return result;
		}
		if (result < 0) 
			return result;

		/* Enable bus-mastering */
		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
		command|=PCI_COMMAND_MASTER;
		pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
		if (!(command&PCI_COMMAND_MASTER)) 
		{
			printk(KERN_ERR "bttv: PCI bus-mastering could not be enabled\n");
			return -1;
		}
		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
			&latency);
		if (!latency) 
		{
			latency=32;
			pcibios_write_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
				latency);
		}
		DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency));
		bttv_num++;
	}
	if(bttv_num)
		printk(KERN_INFO "bttv: %d Bt848 card(s) found.\n", bttv_num);
	return bttv_num;
}

static void release_bttv(void)
{
	u8 command;
	int i;
	struct bttv *btv;

	for (i=0;i<bttv_num; i++) 
	{
		btv=&bttvs[i];
		/* turn off all capturing, DMA and IRQs */

		/* first disable interrupts before unmapping the memory! */
		btwrite(0, BT848_INT_MASK);
		btwrite(0xffffffffUL,BT848_INT_STAT);
		btwrite(0x0, BT848_GPIO_OUT_EN);

		bt848_cap(btv, 0);
    
		/* disable PCI bus-mastering */
		pcibios_read_config_byte(btv->bus, btv->devfn, PCI_COMMAND, &command);
		command|=PCI_COMMAND_MASTER;
		pcibios_write_config_byte(btv->bus, btv->devfn, PCI_COMMAND, command);
    
		/* unmap and free memory */
		if (btv->risc_odd)
			kfree((void *) btv->risc_odd);
			
		if (btv->risc_even)
			kfree((void *) btv->risc_even);

		DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%08x.\n", btv->risc_jmp));
		if (btv->risc_jmp)
			kfree((void *) btv->risc_jmp);

		DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%08x.\n", btv->vbibuf));
		if (btv->vbibuf)
			vfree((void *) btv->vbibuf);
		free_irq(btv->irq,btv);
		DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%08x.\n", btv->bt848_mem));
		if (btv->bt848_mem)
			iounmap(btv->bt848_mem);
		video_unregister_device(&btv->video_dev);
	}
}


#ifdef MODULE

int init_module(void)
{
#else
int init_bttv_cards(struct video_init *unused)
{
#endif
	int i;
  
	handle_chipset();
	if (find_bt848()<0)
		return -EIO;

	for (i=0; i<bttv_num; i++) 
	{
		if (init_bt848(&bttvs[i])<0) 
		{
			release_bttv();
			return -EIO;
		} 
	}  
	return 0;
}

#ifdef MODULE

void cleanup_module(void)
{
	release_bttv();
}

#endif
