patch-2.1.94 linux/drivers/char/bttv.c
Next file: linux/drivers/char/bttv.h
Previous file: linux/drivers/char/bt848.h
Back to the patch index
Back to the overall index
- Lines: 2333
- Date:
Wed Apr 8 17:24:48 1998
- Orig file:
v2.1.93/linux/drivers/char/bttv.c
- Orig date:
Mon Feb 23 18:12:04 1998
diff -u --recursive --new-file v2.1.93/linux/drivers/char/bttv.c linux/drivers/char/bttv.c
@@ -1,7 +1,8 @@
/*
bttv - Bt848 frame grabber driver
- Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
+ & Marcus Metzler (mocm@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
@@ -20,10 +21,20 @@
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.
+
+ TODO:
+
+ * think of some good ioctls for Video4Linux to handle
+ YUV, planar YUV, ... grabs and sell them to AC :-)
+ * move norm from tuner to channel struct!?
+ composite source from a satellite tuner can deliver different norms
+ depending on tuned channel
+ * mmap VBI data?
*/
#include <linux/module.h>
#include <linux/bios32.h>
+#include <linux/config.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
@@ -39,21 +50,31 @@
#include <linux/sched.h>
#include <asm/segment.h>
#include <linux/types.h>
-#include <linux/videodev.h>
+#include <linux/wrapper.h>
+#include <linux/videodev.h>
#include <linux/version.h>
#include <asm/uaccess.h>
+#include "i2c.h"
#include "bttv.h"
#include "tuner.h"
#define DEBUG(x) /* Debug driver */
-#define IDEBUG(x) /* Debug interrupt handler */
+#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 unsigned int remap=0; /* remap Bt848 */
+static unsigned int vidmem=0; /* manually set video mem address */
+static int triton1=0;
+static int radio=0;
+
+static unsigned int card=0;
+
+MODULE_PARM(remap,"i");
+MODULE_PARM(vidmem,"i");
+MODULE_PARM(triton1,"i");
+MODULE_PARM(radio,"i");
+MODULE_PARM(card,"i");
static int find_vga(void);
static void bt848_set_risc_jmps(struct bttv *btv);
@@ -61,17 +82,21 @@
/* Anybody who uses more than four? */
#define BTTV_MAX 4
-static int bttv_num;
+static int bttv_num; /* number of Bt848s in use */
static struct bttv bttvs[BTTV_MAX];
#define I2C_TIMING (0x7<<4)
#define I2C_COMMAND (I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA)
+#define I2C_DELAY 10
+#define I2C_SET(CTRL,DATA) \
+ { btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); }
+#define I2C_GET() (btread(BT848_I2C)&1)
+
#define AUDIO_MUTE_DELAY 10000
#define FREQ_CHANGE_DELAY 20000
#define EEPROM_WRITE_DELAY 20000
-
/*******************************/
/* Memory management functions */
/*******************************/
@@ -79,56 +104,137 @@
/* 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)
+static inline unsigned long uvirt_to_phys(unsigned long 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));
+ ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/);
pte = *ptep;
if(pte_present(pte))
- return (pte_page(pte)|(adr&(PAGE_SIZE-1)));
+ return
+ virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1))));
return 0;
}
+static inline unsigned long uvirt_to_bus(unsigned long adr)
+{
+ /* printk("adr: 0x%8x, ",adr);
+ printk("phys: 0x%8x, ",(uvirt_to_phys(adr)));
+ printk("bus: 0x%8x\n",virt_to_bus(phys_to_virt(uvirt_to_phys(adr))));
+ */
+ return virt_to_bus(phys_to_virt(uvirt_to_phys(adr)));
+}
+
/* 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)
+static inline unsigned long kvirt_to_phys(unsigned long adr)
{
return uvirt_to_phys(VMALLOC_VMADDR(adr));
}
-static inline ulong kvirt_to_bus(ulong adr)
+static inline unsigned long kvirt_to_bus(unsigned long adr)
{
- return virt_to_bus(phys_to_virt(kvirt_to_phys(adr)));
+ return uvirt_to_bus(VMALLOC_VMADDR(adr));
}
+static void * rvmalloc(unsigned long size)
+{
+ void * mem;
+ unsigned long adr, page;
+
+ mem=vmalloc(size);
+ if (mem)
+ {
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(adr);
+ mem_map_reserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ size-=PAGE_SIZE;
+ }
+ }
+ return mem;
+}
-/*****************/
-/* I2C functions */
-/*****************/
+static void rvfree(void * mem, unsigned long size)
+{
+ unsigned long adr, page;
+
+ if (mem)
+ {
+ adr=(unsigned long) mem;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(adr);
+ mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+ adr+=PAGE_SIZE;
+ size-=PAGE_SIZE;
+ }
+ vfree(mem);
+ }
+}
-static int I2CRead(struct bttv *btv, int addr)
+/*
+ * 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 *) rvmalloc(2*0x144000);
+ else
+ printk(KERN_ERR "bttv: Double alloc of fbuffer!\n");
+ if(!btv->fbuffer)
+ return -ENOBUFS;
+ return 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+/* I2C functions */
+
+/* software I2C functions */
+
+static void i2c_setlines(struct i2c_bus *bus,int ctrl,int data)
+{
+ struct bttv *btv = (struct bttv*)bus->data;
+ btwrite((ctrl<<1)|data, BT848_I2C);
+ udelay(I2C_DELAY);
+}
+
+static int i2c_getdataline(struct i2c_bus *bus)
+{
+ struct bttv *btv = (struct bttv*)bus->data;
+ return btread(BT848_I2C)&1;
+}
+
+/* hardware I2C functions */
+
+/* read I2C */
+static int I2CRead(struct i2c_bus *bus, unsigned char addr)
{
u32 i;
u32 stat;
+ struct bttv *btv = (struct bttv*)bus->data;
/* 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!)
*/
@@ -136,11 +242,12 @@
{
stat=btread(BT848_INT_STAT);
if (stat & BT848_INT_I2CDONE)
- break;
- udelay(1000); /* 1ms, as I2C is 1kHz (?) */
+ break;
+ udelay(1000);
}
- if (!i) {
+ if (!i)
+ {
printk(KERN_DEBUG "bttv: I2CRead timeout\n");
return -1;
}
@@ -153,12 +260,13 @@
/* 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)
+static int I2CWrite(struct i2c_bus *bus, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both)
{
u32 i;
u32 data;
u32 stat;
+ struct bttv *btv = (struct bttv*)bus->data;
/* clear status bit; BT848_INT_RACK is ro */
btwrite(BT848_INT_I2CDONE, BT848_INT_STAT);
@@ -172,15 +280,16 @@
btwrite(data, BT848_I2C);
- for (i=1000; i; i--)
+ for (i=0x1000; i; i--)
{
stat=btread(BT848_INT_STAT);
if (stat & BT848_INT_I2CDONE)
break;
- udelay(1000);
+ udelay(1000);
}
- if (!i) {
+ if (!i)
+ {
printk(KERN_DEBUG "bttv: I2CWrite timeout\n");
return -1;
}
@@ -190,19 +299,20 @@
return 0;
}
-static void readee(struct bttv *btv, unchar *eedata)
+/* read EEPROM */
+static void readee(struct i2c_bus *bus, unsigned char *eedata)
{
int i, k;
-
- if (I2CWrite(btv, 0xa0, 0, -1, 0)<0)
+
+ if (I2CWrite(bus, 0xa0, 0, -1, 0)<0)
{
printk(KERN_WARNING "bttv: readee error\n");
return;
}
-
+
for (i=0; i<256; i++)
{
- k=I2CRead(btv, 0xa1);
+ k=I2CRead(bus, 0xa1);
if (k<0)
{
printk(KERN_WARNING "bttv: readee error\n");
@@ -212,13 +322,14 @@
}
}
-static void writeee(struct bttv *btv, unchar *eedata)
+/* write EEPROM */
+static void writeee(struct i2c_bus *bus, unsigned char *eedata)
{
int i;
for (i=0; i<256; i++)
{
- if (I2CWrite(btv, 0xa0, i, eedata[i], 1)<0)
+ if (I2CWrite(bus, 0xa0, i, eedata[i], 1)<0)
{
printk(KERN_WARNING "bttv: writeee error (%d)\n", i);
break;
@@ -227,28 +338,91 @@
}
}
+void attach_inform(struct i2c_bus *bus, int id)
+{
+ struct bttv *btv = (struct bttv*)bus->data;
+ int tunertype;
+
+ switch (id)
+ {
+ case I2C_DRIVERID_MSP3400:
+ btv->have_msp3400 = 1;
+ break;
+ case I2C_DRIVERID_TUNER:
+ btv->have_tuner = 1;
+ if (btv->type == BTTV_MIRO)
+ {
+ tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7;
+ i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER,
+ TUNER_SET_TYPE,&tunertype);
+ }
+ break;
+ }
+}
+
+void detach_inform(struct i2c_bus *bus, int id)
+{
+ struct bttv *btv = (struct bttv*)bus->data;
+
+ switch (id)
+ {
+ case I2C_DRIVERID_MSP3400:
+ btv->have_msp3400 = 0;
+ break;
+ case I2C_DRIVERID_TUNER:
+ btv->have_tuner = 0;
+ break;
+ }
+}
+
+static struct i2c_bus bttv_i2c_bus_template =
+{
+ "bt848",
+ I2C_BUSID_BT848,
+ NULL,
+
+ SPIN_LOCK_UNLOCKED,
+
+ attach_inform,
+ detach_inform,
+
+ i2c_setlines,
+ i2c_getdataline,
+ I2CRead,
+ I2CWrite,
+};
+
+/* ----------------------------------------------------------------------- */
+
/*
- * Tuner, internal, external and mute
+ * Tuner, Radio, internal, external and mute
*/
-static unchar audiomuxs[][4] =
+static unsigned char audiomuxs[][5] =
{
- { 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??? */
+ { 0x00, 0x00, 0x00, 0x00, 0x00}, /* unknown */
+ { 0x02, 0x00, 0x00, 0x00, 0x0a}, /* MIRO */
+ { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Hauppauge */
+ { 0x04, 0x00, 0x02, 0x03, 0x01}, /* STB */
+ { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Intel??? */
+ { 0x00, 0x01, 0x02, 0x03, 0x04}, /* Diamond DTV2000 */
+ { 0x0c, 0x00, 0x0b, 0x0b, 0x00}, /* AVerMedia TVPhone */
};
static void audio(struct bttv *btv, int mode)
{
+ /* enable least significant GPIO output nibble */
btwrite(0x0f, BT848_GPIO_OUT_EN);
+
+ /* select direct input */
btwrite(0x00, BT848_GPIO_REG_INP);
switch (mode)
{
- case AUDIO_UNMUTE:
+ case AUDIO_MUTE:
+ btv->audio|=AUDIO_MUTE;
+ break;
+ case AUDIO_UNMUTE:
btv->audio&=~AUDIO_MUTE;
mode=btv->audio;
break;
@@ -263,8 +437,12 @@
btv->audio|=mode;
break;
}
- if ((btv->audio&AUDIO_MUTE) || !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
- mode=AUDIO_OFF;
+ /* if audio mute or not in H-lock, turn audio off */
+ if ((btv->audio&AUDIO_MUTE) ||
+ (!btv->radio && !(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC)))
+ mode=AUDIO_OFF;
+ if ((mode == 0) && (btv->radio))
+ mode = 1;
btaor(audiomuxs[btv->type][mode] , ~0x0f, BT848_GPIO_DATA);
}
@@ -319,11 +497,19 @@
#define VBIBUF_SIZE 65536
+/* Maximum sample number per VBI line is 2044, can NTSC deliver this?
+ Note that we write 2048-aligned to keep alignment to memory pages
+*/
+#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;
- dword *po=(dword *) btv->vbi_odd;
- dword *pe=(dword *) btv->vbi_even;
+ unsigned int *po=(unsigned int *) btv->vbi_odd;
+ unsigned int *pe=(unsigned int *) 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));
@@ -333,8 +519,8 @@
*(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++)=VBI_RISC;
+ *(po++)=kvirt_to_bus((unsigned long)btv->vbibuf+i*2048);
}
*(po++)=BT848_RISC_JUMP;
*(po++)=virt_to_bus(btv->risc_jmp+4);
@@ -342,11 +528,129 @@
*(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++)=VBI_RISC;
+ *(pe++)=kvirt_to_bus((unsigned long)btv->vbibuf+i*2048);
}
*(pe++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16);
*(pe++)=virt_to_bus(btv->risc_jmp+10);
+ DEBUG(printk(KERN_DEBUG "po: 0x%08x\n",(int)po));
+ DEBUG(printk(KERN_DEBUG "pe: 0x%08x\n",(int)pe));
+}
+
+int fmtbppx2[16] = {
+ 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 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 fmt)
+{
+ unsigned long line;
+ unsigned long bpl; /* bytes per line */
+ unsigned long bl;
+ unsigned long todo;
+ unsigned int **rp;
+ int inter;
+ unsigned long vadr=(unsigned long) vbuf;
+
+ inter = (height>btv->win.cropheight/2) ? 1 : 0;
+ bpl=width*fmtbppx2[fmt&0xf]/2;
+
+ *(ro++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(ro++)=0;
+ *(re++)=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; *(re++)=0;
+
+ for (line=0; line < (height<<(1^inter)); line++)
+ {
+ if (inter)
+ rp= (line&1) ? &re : &ro;
+ else
+ rp= (line>height) ? &re : &ro;
+
+ bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
+ if (bpl<=bl)
+ {
+ *((*rp)++)=BT848_RISC_WRITE|BT848_RISC_SOL|
+ BT848_RISC_EOL|bpl;
+ *((*rp)++)=kvirt_to_bus(vadr);
+ vadr+=bpl;
+ }
+ else
+ {
+ todo=bpl;
+ *((*rp)++)=BT848_RISC_WRITE|BT848_RISC_SOL|bl;
+ *((*rp)++)=kvirt_to_bus(vadr);
+ vadr+=bl;
+ todo-=bl;
+ while (todo>PAGE_SIZE)
+ {
+ *((*rp)++)=BT848_RISC_WRITE|PAGE_SIZE;
+ *((*rp)++)=kvirt_to_bus(vadr);
+ vadr+=PAGE_SIZE;
+ todo-=PAGE_SIZE;
+ }
+ *((*rp)++)=BT848_RISC_WRITE|BT848_RISC_EOL|todo;
+ *((*rp)++)=kvirt_to_bus(vadr);
+ vadr+=todo;
+ }
+ }
+
+ *(ro++)=BT848_RISC_JUMP;
+ *(ro++)=btv->bus_vbi_even;
+ *(re++)=BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16);
+ *(re++)=btv->bus_vbi_odd;
+
+ return 0;
+}
+
+/* does this really make a difference ???? */
+#define BURST_MAX 4096
+
+static inline void write_risc_segment(unsigned int **rp, unsigned long line_adr, unsigned int command,
+ int *x, uint dx, uint bpp, uint width)
+{
+ unsigned int flags, len;
+
+ if (!dx)
+ return;
+ len=dx*bpp;
+
+#ifdef LIMIT_DMA
+ if (command==BT848_RISC_WRITEC)
+ {
+ unsigned int dx2=BURST_MAX/bpp;
+ while (len>BURST_MAX)
+ {
+ write_risc_segment(rp, line_adr, command,
+ &x,dx2, bpp, width);
+ dx-=dx2;
+ len=dx*bpp;
+ }
+ }
+#endif
+
+ /* mask upper 8 bits for 24+8 bit overlay modes */
+ flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0);
+
+ if (*x==0)
+ {
+ if (command==BT848_RISC_SKIP)
+ {
+ if (dx<width)
+ {
+ flags|=BT848_RISC_BYTE_ALL;
+ command=BT848_RISC_WRITE;
+ }
+ }
+ else
+ if (command==BT848_RISC_WRITEC)
+ command=BT848_RISC_WRITE;
+ flags|=BT848_RISC_SOL;
+ }
+ if (*x+dx==width)
+ flags|=BT848_RISC_EOL;
+ *((*rp)++)=command|flags|len;
+ if (command==BT848_RISC_WRITE)
+ *((*rp)++)=line_adr+*x*bpp;
+ *x+=dx;
}
/*
@@ -358,237 +662,264 @@
* www.brooktree.com - nicely done those folks.
*/
-static void bt848_set_size(struct bttv *btv)
+struct tvnorm
+{
+ u16 cropwidth, cropheight;
+ u16 totalwidth;
+ u8 adelay, bdelay, iform;
+ u32 scaledtwidth;
+ u16 hdelayx1, hactivex1;
+ u16 vdelay;
+};
+
+static struct tvnorm tvnorms[] = {
+ /* PAL-BDGHI */
+ { 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
+ 944, 186, 922, 0x20},
+ /* NTSC */
+ { 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
+ 780, 135, 754, 0x16},
+ /* SECAM */
+ { 768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
+ 944, 186, 922, 0x20},
+ /* PAL-M */
+ { 640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
+ 780, 186, 922, 0x16},
+ /* PAL-N */
+ { 768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
+ 944, 186, 922, 0x20},
+};
+#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
+
+
+/* set geometry for even/odd frames
+ just if you are wondering:
+ handling of even and odd frames will be separated, e.g. for grabbing
+ the even ones as RGB into videomem and the others as YUV in main memory for
+ compressing and sending to the video conferencing partner.
+
+*/
+static inline void bt848_set_eogeo(struct bttv *btv, int odd, u8 vtc,
+ u16 hscale, u16 vscale,
+ u16 hactive, u16 vactive,
+ u16 hdelay, u16 vdelay,
+ u8 crop)
+{
+ int off = odd ? 0x80 : 0x00;
+
+ 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), 0xc0, BT848_E_VSCALE_HI+off);
+ btwrite(vscale&0xff, BT848_E_VSCALE_LO+off);
+ btwrite(hactive&0xff, BT848_E_HACTIVE_LO+off);
+ btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off);
+ btwrite(vactive&0xff, BT848_E_VACTIVE_LO+off);
+ btwrite(vdelay&0xff, BT848_E_VDELAY_LO+off);
+ btwrite(crop, BT848_E_CROP+off);
+}
+
+
+static void bt848_set_geo(struct bttv *btv, u16 width, u16 height, u16 fmt)
{
- u16 vscale, hscale;
+ 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;
+ u8 crop, vtc;
+ struct tvnorm *tvn;
+
+ if (!width || !height)
+ return;
- 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;
- }
+ tvn=&tvnorms[btv->win.norm];
- /*
- * 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);
- }
+ if (btv->win.cropwidth>tvn->cropwidth)
+ btv->win.cropwidth=tvn->cropwidth;
+ if (btv->win.cropheight>tvn->cropheight)
+ btv->win.cropheight=tvn->cropheight;
+
+ if (width>btv->win.cropwidth)
+ width=btv->win.cropwidth;
+ if (height>btv->win.cropheight)
+ height=btv->win.cropheight;
+
+ btwrite(tvn->adelay, BT848_ADELAY);
+ btwrite(tvn->bdelay, BT848_BDELAY);
+ btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM);
+
+ btwrite(fmt, BT848_COLOR_FMT);
+ hactive=width;
+
+ vtc=0;
+ /* Some people say interpolation looks bad ... */
+ /* vtc = (hactive < 193) ? 2 : ((hactive < 385) ? 1 : 0); */
+
+ btv->win.interlace = (height>btv->win.cropheight/2) ? 1 : 0;
+ inter=(btv->win.interlace&1)^1;
+ xsf = (hactive*tvn->scaledtwidth)/btv->win.cropwidth;
+ hscale = ((tvn->totalwidth*4096UL)/xsf-4096);
+ vdelay=btv->win.cropy+tvn->vdelay;
+
+ hdelay=(tvn->hdelayx1*tvn->scaledtwidth)/tvn->totalwidth;
+ hdelay=((hdelay+btv->win.cropx)*hactive)/btv->win.cropwidth;
+ hdelay&=0x3fe;
- /*
- * 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;
+ sr=((btv->win.cropheight>>inter)*512)/height-512;
vscale=(0x10000UL-sr)&0x1fff;
vactive=btv->win.cropheight;
+ crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)|
+ ((vactive>>4)&0x30)|((vdelay>>2)&0xc0);
+ vscale|= btv->win.interlace ? (BT848_VSCALE_INT<<8) : 0;
+
+ bt848_set_eogeo(btv, 0, vtc, hscale, vscale, hactive, vactive,
+ hdelay, vdelay, crop);
+ bt848_set_eogeo(btv, 1, vtc, hscale, vscale, hactive, vactive,
+ hdelay, vdelay, crop);
+
+}
-#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
+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;
+ int bpp;
+
+ bpp=fmtbppx2[btv->win.color_fmt&0x0f]/2;
+ if (btv->win.bpp == 0)
{
- btand(~BT848_VSCALE_INT, BT848_E_VSCALE_HI);
- btand(~BT848_VSCALE_INT, BT848_O_VSCALE_HI);
+ btv->win.bpp=bpp;
+ format=btv->win.color_fmt;
}
-
- /*
- * Load her up
+ else if (btv->win.bpp!=bpp)
+ btv->win.color_fmt=format=bpp2fmt[(btv->win.bpp-1)&3];
+ else
+ format=btv->win.color_fmt;
+
+ /* 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??? [AC - because its a standard format so I guess
+ * it never occured them]
+ * Enable dithering in this mode
*/
-
- 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);
+ if (format==BT848_COLOR_FMT_RGB8)
+ btand(~0x10, BT848_CAP_CTL);
+ else
+ btor(0x10, BT848_CAP_CTL);
- crop=((hactive>>8)&0x03)|((hdelay>>6)&0x0c)|
- ((vactive>>4)&0x30)|((vdelay>>2)&0xc0);
- btwrite(crop, BT848_E_CROP);
- btwrite(crop, BT848_O_CROP);
+ bt848_set_geo(btv,btv->win.width, btv->win.height, format);
}
-
-/*
- * 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)
+static void set_freq(struct bttv *btv, unsigned short freq)
{
- u8 config;
- u16 div;
- struct tunertype *tun=&tuners[btv->tuner];
+ int fixme = freq; /* XXX */
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 (btv->radio)
+ {
+ if (btv->have_tuner)
+ i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER,
+ TUNER_SET_RADIOFREQ,&fixme);
- 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))
+ if (btv->have_msp3400) {
+ i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
+ MSP_SET_RADIO,0);
+ i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
+ MSP_NEWCHANNEL,0);
+ }
+ }
+ else
{
+ if (btv->have_tuner)
+ i2c_control_device(&(btv->i2c), I2C_DRIVERID_TUNER,
+ TUNER_SET_TVFREQ,&fixme);
+
+ if (btv->have_msp3400) {
+ i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
+ MSP_SET_TVNORM,&(btv->win.norm));
+ i2c_control_device(&(btv->i2c),I2C_DRIVERID_MSP3400,
+ MSP_NEWCHANNEL,0);
+ }
+ }
+
+ if (!(oldAudio & AUDIO_MUTE)) {
udelay(FREQ_CHANGE_DELAY);
audio(btv, AUDIO_UNMUTE);
}
}
+
+
+/*
+ * Grab into virtual memory.
+ * Currently only does double buffering. Do we need more?
+ */
+
+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;
+ }
+
+ /*
+ * No grabbing past the end of the buffer!
+ */
+
+ if(mp->frame>1 || mp->frame <0)
+ return -EINVAL;
+
+ if(mp->height <0 || mp->width <0)
+ return -EINVAL;
+
+ if(mp->height>480 || mp->width>640)
+ return -EINVAL;
+
+ /*
+ * FIXME: Check the format of the grab here. This is probably
+ * also less restrictive than the normal overlay grabs. Stuff
+ * like QCIF has meaning as a capture.
+ */
+
+ /*
+ * Ok load up the BT848
+ */
+
+ vbuf=(unsigned int *)(btv->fbuffer+0x144000*mp->frame);
+ if (!(btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC))
+ return -EAGAIN;
+ ro=btv->grisc+(((btv->grabcount++)&1) ? 2048 :0);
+ re=ro+1024;
+ btv->gwidth=mp->width;
+ btv->gheight=mp->height;
+ btv->gfmt=mp->format;
+ make_vrisctab(btv, ro, re, vbuf, btv->gwidth, btv->gheight, btv->gfmt);
+ /* bt848_set_risc_jmps(btv); */
+ btor(3, BT848_CAP_CTL);
+ btor(3, BT848_GPIO_DMA_CTL);
+ btv->gro=virt_to_bus(ro);
+ btv->gre=virt_to_bus(re);
+ if (!(btv->grabbing++))
+ btv->risc_jmp[12]=BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ;
+ /* interruptible_sleep_on(&btv->capq); */
+ return 0;
+}
static long bttv_write(struct video_device *v, const char *buf, unsigned long count, int nonblock)
{
@@ -608,7 +939,6 @@
todo-=q;
buf+=q;
-/* btv->vbip=0; */
cli();
if (todo && q==VBIBUF_SIZE-btv->vbip)
{
@@ -659,14 +989,17 @@
users+=bttvs[i].user;
if (users==1)
find_vga();
+ btv->fbuffer=NULL;
+ if (!btv->fbuffer)
+ btv->fbuffer=(unsigned char *) rvmalloc(2*0x144000);
+ if (!btv->fbuffer)
+ {
+ btv->user--;
+ return -EINVAL;
+ }
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;
@@ -679,17 +1012,15 @@
btv->user--;
audio(btv, AUDIO_MUTE);
btv->cap&=~3;
-#if 0 /* FIXME */
- if(minor&0x20)
- {
- btv->cap&=~0x0c;
- }
-#endif
bt848_set_risc_jmps(btv);
+ if(btv->fbuffer)
+ rvfree((void *) btv->fbuffer, 2*0x144000);
+ btv->fbuffer=0;
MOD_DEC_USE_COUNT;
}
+
/***********************************/
/* ioctls and supporting functions */
/***********************************/
@@ -714,7 +1045,7 @@
btaor(conthi, ~4, BT848_O_CONTROL);
}
-extern inline void bt848_sat_u(struct bttv *btv, ulong data)
+extern inline void bt848_sat_u(struct bttv *btv, unsigned long data)
{
u32 datahi;
@@ -724,7 +1055,7 @@
btaor(datahi, ~2, BT848_O_CONTROL);
}
-static inline void bt848_sat_v(struct bttv *btv, ulong data)
+static inline void bt848_sat_v(struct bttv *btv, unsigned long data)
{
u32 datahi;
@@ -734,6 +1065,7 @@
btaor(datahi, ~1, BT848_O_CONTROL);
}
+
/*
* Cliprect -> risc table.
*
@@ -772,15 +1104,17 @@
}
first2.next=NULL;
- rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem[rpo++]=0;
+ rmem[rpo++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1|(0xf<<20);
+ rmem[rpo++]=0;
- rmem2[rpe++]=BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1; rmem2[rpe++]=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
@@ -842,7 +1176,7 @@
* here, but the overlap might be partial
*/
- /* add rect to second (x-sorted) list if rect.y == y */
+ /* add rect to second (x-sorted) list if rect.y == y */
if ((cur=first.next))
{
while ((cur) && (cur->y == y))
@@ -953,8 +1287,9 @@
{
/* 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;
+ rpp[(*rp)-1]=BT848_RISC_SKIP|
+ (btv->win.width*depth)|
+ BT848_RISC_EOL|BT848_RISC_SOL;
}
}
/*
@@ -988,15 +1323,15 @@
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;
@@ -1092,10 +1427,14 @@
/* Only channel 0 has a tuner */
if(v.tuner!=0 || lastchan)
return -EINVAL;
- if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC)
+ if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC
+ &&v.mode!=VIDEO_MODE_SECAM)
return -EOPNOTSUPP;
+ /* FIXME: norm should be in video_channel struct
+ composite source can have different norms too
+ */
btv->win.norm = v.mode;
- bt848_set_size(btv);
+ bt848_set_winsize(btv);
return 0;
}
case VIDIOCGPICT:
@@ -1109,6 +1448,7 @@
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;
@@ -1155,7 +1495,7 @@
on=(btv->cap&3)?1:0;
bt848_cap(btv,0);
- bt848_set_size(btv);
+ bt848_set_winsize(btv);
if(vw.clipcount>256)
return -EDOM; /* Too many! */
@@ -1188,7 +1528,7 @@
*/
write_risc_data(btv,vcp, vw.clipcount);
vfree(vcp);
- if(on)
+ if(on && btv->win.vidadr!=0)
bt848_cap(btv,1);
return 0;
}
@@ -1213,14 +1553,15 @@
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
{
+ if(btv->win.vidadr==0 || btv->win.width==0
+ || btv->win.height==0)
+ return -EINVAL;
bt848_cap(btv,1);
}
return 0;
@@ -1247,7 +1588,13 @@
return -EFAULT;
if(v.depth!=8 && v.depth!=16 && v.depth!=24 && v.depth!=32)
return -EINVAL;
- btv->win.vidadr=(int)v.base;
+ if (v.base)
+ /* also handle virtual base addresses */
+ if ((unsigned int)v.base>=0xe0000000UL)
+ btv->win.vidadr=(uint)v.base;
+ else
+ btv->win.vidadr=PAGE_OFFSET|
+ uvirt_to_bus((uint)v.base);
btv->win.sheight=v.height;
btv->win.swidth=v.width;
btv->win.bpp=v.depth/8;
@@ -1255,7 +1602,7 @@
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);
+ bt848_set_winsize(btv);
return 0;
}
case VIDIOCKEY:
@@ -1287,6 +1634,17 @@
v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE);
v.flags|=VIDEO_AUDIO_MUTABLE;
strcpy(v.name,"TV");
+ if (btv->have_msp3400)
+ {
+ v.flags|=VIDEO_AUDIO_VOLUME;
+ i2c_control_device(&(btv->i2c),
+ I2C_DRIVERID_MSP3400,
+ MSP_GET_VOLUME,&(v.volume));
+ i2c_control_device(&(btv->i2c),
+ I2C_DRIVERID_MSP3400,
+ MSP_GET_STEREO,&(v.mode));
+ }
+ else v.mode = VIDEO_SOUND_MONO;
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
return 0;
@@ -1296,28 +1654,55 @@
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.audio<0||v.audio>2)
+ return -EINVAL;
+ bt848_muxsel(btv,v.audio);
if(!(v.flags&VIDEO_AUDIO_MUTE))
audio(btv, AUDIO_UNMUTE);
+ if (btv->have_msp3400)
+ {
+ i2c_control_device(&(btv->i2c),
+ I2C_DRIVERID_MSP3400,
+ MSP_SET_VOLUME,&(v.volume));
+ i2c_control_device(&(btv->i2c),
+ I2C_DRIVERID_MSP3400,
+ MSP_SET_STEREO,&(v.mode));
+ }
btv->audio_dev=v;
return 0;
}
- case BTTV_WRITEEE:
+ case VIDIOCSYNC:
+ if (btv->grabbing && btv->grab==btv->lastgrab)
+ interruptible_sleep_on(&btv->capq);
+ btv->lastgrab=btv->grab;
+ return 0;
+
+ case BTTV_WRITEE:
+ if(!suser())
+ return -EPERM;
if(copy_from_user((void *) eedata, (void *) arg, 256))
return -EFAULT;
- writeee(btv, eedata);
- break;
+ writeee(&(btv->i2c), eedata);
+ return 0;
case BTTV_READEE:
- readee(btv, eedata);
+ if(!suser())
+ return -EPERM;
+ readee(&(btv->i2c), eedata);
if(copy_to_user((void *) arg, (void *) eedata, 256))
return -EFAULT;
break;
+ case VIDIOCMCAPTURE:
+ {
+ struct video_mmap vm;
+ if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
+ return -EFAULT;
+ return vgrab(btv, &vm);
+ }
default:
return -ENOIOCTLCMD;
}
@@ -1329,6 +1714,41 @@
return 0;
}
+/*
+ * This maps the vmalloced and reserved fbuffer to user space.
+ *
+ * FIXME:
+ * - PAGE_READONLY should suffice!?
+ * - remap_page_range is kind of inefficient for page by page remapping.
+ * But e.g. pte_alloc() does not work in modules ... :-(
+ */
+
+static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+ struct bttv *btv=(struct bttv *)dev;
+ unsigned long start=(unsigned long) adr;
+ unsigned long page,pos;
+
+ if (size>2*0x144000)
+ return -EINVAL;
+ if (!btv->fbuffer)
+ {
+ if(fbuffer_alloc(btv))
+ return -EINVAL;
+ }
+ pos=(unsigned long) btv->fbuffer;
+ while (size > 0)
+ {
+ page = kvirt_to_phys(pos);
+ if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+ start+=PAGE_SIZE;
+ pos+=PAGE_SIZE;
+ size-=PAGE_SIZE;
+ }
+ return 0;
+}
+
static struct video_device bttv_template=
{
"UNSET",
@@ -1339,6 +1759,96 @@
bttv_read,
bttv_write,
bttv_ioctl,
+ bttv_mmap,
+ bttv_init_done,
+ NULL,
+ 0,
+ 0
+};
+
+
+static long vbi_read(struct video_device *v, char *buf, unsigned long count,
+ int nonblock)
+{
+ struct bttv *btv=(struct bttv *)(v-2);
+ int q,todo;
+
+ 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;
+
+ 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;
+}
+
+static int vbi_open(struct video_device *dev, int flags)
+{
+ struct bttv *btv=(struct bttv *)(dev-2);
+
+ btv->vbip=VBIBUF_SIZE;
+ btv->cap|=0x0c;
+ bt848_set_risc_jmps(btv);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void vbi_close(struct video_device *dev)
+{
+ struct bttv *btv=(struct bttv *)(dev-2);
+
+ btv->cap&=~0x0c;
+ bt848_set_risc_jmps(btv);
+
+ MOD_DEC_USE_COUNT;
+}
+
+
+static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ return -EINVAL;
+}
+
+static struct video_device vbi_template=
+{
+ "bttv vbi",
+ VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
+ VID_HARDWARE_BT848,
+ vbi_open,
+ vbi_close,
+ vbi_read,
+ bttv_write,
+ vbi_ioctl,
NULL, /* no mmap yet */
bttv_init_done,
NULL,
@@ -1346,9 +1856,106 @@
0
};
+
+static int radio_open(struct video_device *dev, int flags)
+{
+ struct bttv *btv = (struct bttv *)(dev-1);
+
+ if (btv->user)
+ return -EBUSY;
+ btv->user++;
+ set_freq(btv,400*16);
+ btv->radio = 1;
+ bt848_muxsel(btv,0);
+ audio(btv, AUDIO_UNMUTE);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void radio_close(struct video_device *dev)
+{
+ struct bttv *btv=(struct bttv *)(dev-1);
+
+ btv->user--;
+ btv->radio = 0;
+ audio(btv, AUDIO_MUTE);
+ MOD_DEC_USE_COUNT;
+}
+
+static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock)
+{
+ return -EINVAL;
+}
+
+static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+ struct bttv *btv=(struct bttv *)(dev-1);
+ static int lastchan=0;
+ switch (cmd) {
+ case VIDIOCGCAP:
+ /* XXX */
+ break;
+ 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, "Radio");
+ v.rangelow=(int)(87.5*16);
+ v.rangehigh=(int)(108.0*16);
+ v.flags= 0; /* XXX */
+ v.mode = 0; /* XXX */
+ if(copy_to_user(arg,&v,sizeof(v)))
+ return -EFAULT;
+ return 0;
+ }
+ case VIDIOCSTUNER:
+ {
+ struct video_tuner v;
+ if(copy_from_user(&v, arg, sizeof(v)))
+ return -EFAULT;
+ /* Only channel 0 has a tuner */
+ if(v.tuner!=0 || lastchan)
+ return -EINVAL;
+ /* XXX anything to do ??? */
+ return 0;
+ }
+ case VIDIOCGFREQ:
+ case VIDIOCSFREQ:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
+ bttv_ioctl((struct video_device *)btv,cmd,arg);
+ break;
+ default:
+ return -ENOIOCTLCMD;
+ }
+ return 0;
+}
+
+static struct video_device radio_template=
+{
+ "bttv radio",
+ VID_TYPE_TUNER,
+ VID_HARDWARE_BT848,
+ radio_open,
+ radio_close,
+ radio_read, /* just returns -EINVAL */
+ bttv_write, /* just returns -EINVAL */
+ radio_ioctl,
+ NULL, /* no mmap */
+ bttv_init_done, /* just returns 0 */
+ NULL,
+ 0,
+ 0
+};
+
+
struct vidbases
{
- ushort vendor, device;
+ unsigned short vendor, device;
char *name;
uint badr;
};
@@ -1386,7 +1993,7 @@
static int find_vga(void)
{
unsigned int devfn, class, vendev;
- ushort vendor, device, badr;
+ unsigned short vendor, device, badr;
int found=0, bus=0, i, tga_type;
unsigned int vidadr=0;
@@ -1470,6 +2077,8 @@
return found;
}
+
+
#define TRITON_PCON 0x50
#define TRITON_BUS_CONCURRENCY (1<<0)
#define TRITON_STREAMING (1<<1)
@@ -1480,28 +2089,27 @@
{
int index;
+ /* Just in case some nut set this to something dangerous */
+ if (triton1)
+ triton1=BT848_INT_ETBF;
+
for (index = 0; index < 8; index++)
{
unsigned char bus, devfn;
- unsigned char b, bo;
+ unsigned char b;
/* 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))
+ 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))
+ 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, "));
@@ -1512,139 +2120,176 @@
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));
-
+ triton1=BT848_INT_ETBF;
+
+#if 0
+ /* The ETBF bit SHOULD make all this unnecessary */
/* 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;
- }
+ {
+ unsigned char bo;
- /* 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;
- }
+ pcibios_read_config_byte(bus, devfn, TRITON_PCON, &b);
+ bo=b;
+ DEBUG(printk(KERN_DEBUG "bttv: 82437FX: PCON: 0x%x\n",b));
- if (b!=bo)
- {
- pcibios_write_config_byte(bus, devfn, TRITON_PCON, b);
- printk(KERN_DEBUG "bttv: 82437FX: PCON changed to: 0x%x\n",b);
+ if(!(b & TRITON_BUS_CONCURRENCY))
+ {
+ printk(KERN_WARNING "bttv: 82437FX: disabling bus concurrency\n");
+ b |= TRITON_BUS_CONCURRENCY;
+ }
+
+ 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);
+ }
}
+#endif
}
}
}
-static void init_tda9850(struct bttv *btv)
+
+static void init_tda9850(struct i2c_bus *bus)
{
- I2CWrite(btv, I2C_TDA9850, TDA9850_CON3, 0, 1);
+ I2CWrite(bus, I2C_TDA9850, TDA9850_CON1, 0x08, 1); /* noise threshold st */
+ I2CWrite(bus, I2C_TDA9850, TDA9850_CON2, 0x08, 1); /* noise threshold sap */
+ I2CWrite(bus, I2C_TDA9850, TDA9850_CON3, 0x40, 1); /* stereo mode */
+ I2CWrite(bus, I2C_TDA9850, TDA9850_CON4, 0x07, 1); /* 0 dB input gain?*/
+ I2CWrite(bus, I2C_TDA9850, TDA9850_ALI1, 0x10, 1); /* wideband alignment? */
+ I2CWrite(bus, I2C_TDA9850, TDA9850_ALI2, 0x10, 1); /* spectral alignment? */
+ I2CWrite(bus, I2C_TDA9850, TDA9850_ALI3, 0x03, 1);
}
+
+
/* Figure out card and tuner type */
static void idcard(struct bttv *btv)
{
- int i;
-
+ int tunertype;
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)
+ /* Default the card to the user-selected one. */
+ btv->type=card;
+
+ /* If we were asked to auto-detect, then do so!
+ Right now this will only recognize Miro, Hauppauge or STB
+ */
+ if (btv->type == BTTV_UNKNOWN)
{
- if (I2CRead(btv, i)>=0)
- {
- btv->tuneradr=i;
- break;
- }
+ btv->type=BTTV_MIRO;
+
+ if (I2CRead(&(btv->i2c), I2C_HAUPEE)>=0)
+ btv->type=BTTV_HAUPPAUGE;
+ else
+ if (I2CRead(&(btv->i2c), I2C_STBEE)>=0)
+ btv->type=BTTV_STB;
}
- btv->dbx = I2CRead(btv, I2C_TDA9850) ? 0 : 1;
+ btv->dbx = I2CRead(&(btv->i2c), I2C_TDA9850) ? 0 : 1;
if (btv->dbx)
- init_tda9850(btv);
+ init_tda9850(&(btv->i2c));
/* 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");
+ printk("MIRO\n");
+ if (btv->have_tuner)
+ {
+ tunertype=((btread(BT848_GPIO_DATA)>>10)-1)&7;
+ i2c_control_device(&(btv->i2c),
+ I2C_DRIVERID_TUNER,
+ TUNER_SET_TYPE,&tunertype);
+ }
strcpy(btv->video_dev.name,"BT848(Miro)");
break;
case BTTV_HAUPPAUGE:
- printk("HAUPPAUGE");
+ printk("HAUPPAUGE\n");
strcpy(btv->video_dev.name,"BT848(Hauppauge)");
break;
case BTTV_STB:
- printk("STB");
+ printk("STB\n");
strcpy(btv->video_dev.name,"BT848(STB)");
break;
case BTTV_INTEL:
- printk("Intel");
+ printk("Intel\n");
strcpy(btv->video_dev.name,"BT848(Intel)");
break;
case BTTV_DIAMOND:
- printk("Diamond");
+ printk("Diamond\n");
strcpy(btv->video_dev.name,"BT848(Diamond)");
break;
+ case BTTV_AVERMEDIA:
+ printk("AVerMedia\n");
+ strcpy(btv->video_dev.name,"BT848(AVerMedia)");
+ 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;
-
+
+ /* Sync to start of odd field */
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;
+ /* Jump to odd vbi sub */
+ btv->risc_jmp[2]=BT848_RISC_JUMP|(0x5<<20);
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;
+ /* Jump to odd sub */
+ btv->risc_jmp[4]=BT848_RISC_JUMP|(0x6<<20);
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);
+
+ /* Sync to start of even field */
btv->risc_jmp[6]=BT848_RISC_SYNC|BT848_RISC_RESYNC|BT848_FIFO_STATUS_VRO;
btv->risc_jmp[7]=0;
+ /* Jump to even vbi sub */
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;
+ /* Jump to even sub */
+ btv->risc_jmp[10]=BT848_RISC_JUMP|(8<<20);
if (flags&1)
btv->risc_jmp[11]=virt_to_bus(btv->risc_even);
else
- btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp);
+ btv->risc_jmp[11]=virt_to_bus(btv->risc_jmp+12);
+
+ btv->risc_jmp[12]=BT848_RISC_JUMP;
+ btv->risc_jmp[13]=virt_to_bus(btv->risc_jmp);
+ /* enable cpaturing and DMA */
btaor(flags, ~0x0f, BT848_CAP_CTL);
if (flags&0x0f)
bt848_dma(btv, 3);
@@ -1653,12 +2298,15 @@
}
-static int init_bt848(struct bttv *btv)
+static int init_bt848(int i)
{
- /* reset the bt848 */
- btwrite(0,BT848_SRESET);
+ struct bttv *btv = &bttvs[i];
+
btv->user=0;
+ /* reset the bt848 */
+ btwrite(0, BT848_SRESET);
+
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 */
@@ -1674,43 +2322,66 @@
btv->win.cropx=0;
btv->win.cropy=0;
btv->win.bpp=2;
+ btv->win.color_fmt=BT848_COLOR_FMT_RGB16;
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)))
+ btv->gmode=0;
+ btv->risc_odd=0;
+ btv->risc_even=0;
+ btv->risc_jmp=0;
+ btv->vbibuf=0;
+ btv->grisc=0;
+ btv->grabbing=0;
+ btv->grabcount=0;
+ btv->grab=0;
+ btv->lastgrab=0;
+
+ /* i2c */
+ memcpy(&(btv->i2c),&bttv_i2c_bus_template,sizeof(struct i2c_bus));
+ sprintf(btv->i2c.name,"bt848-%d",i);
+ btv->i2c.data = btv;
+
+ if (!(btv->risc_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
return -1;
- if (!(btv->risc_even=(dword *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
+ if (!(btv->risc_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
return -1;
- if (!(btv->risc_jmp =(dword *) kmalloc(2048, GFP_KERNEL)))
+ if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL)))
return -1;
- btv->vbi_odd=btv->risc_jmp+12;
+ DEBUG(printk(KERN_DEBUG "risc_jmp: %p\n",btv->risc_jmp));
+ btv->vbi_odd=btv->risc_jmp+16;
btv->vbi_even=btv->vbi_odd+256;
- btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp);
+ btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12);
btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6);
btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD);
- btv->vbibuf=(unchar *) vmalloc(VBIBUF_SIZE);
+ btv->vbibuf=(unsigned char *) vmalloc(VBIBUF_SIZE);
if (!btv->vbibuf)
return -1;
+ if (!(btv->grisc=(unsigned int *) kmalloc(16384, GFP_KERNEL)))
+ return -1;
+
+ btv->fbuffer=NULL;
bt848_muxsel(btv, 1);
- bt848_set_size(btv);
+ bt848_set_winsize(btv);
/* btwrite(0, BT848_TDEC); */
btwrite(0x10, BT848_COLOR_CTL);
btwrite(0x00, BT848_CAP_CTL);
+ btwrite(0xfc, BT848_GPIO_DMA_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);
+ bt848_bright(btv, 0x10);
btwrite(0x60, BT848_E_VSCALE_HI);
btwrite(0x60, BT848_O_VSCALE_HI);
@@ -1719,19 +2390,27 @@
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;
+
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|
+ /* clear interrupt status */
+ btwrite(0xfffffUL, BT848_INT_STAT);
+
+ /* set interrupt mask */
+ btwrite(triton1|
+/* BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|
+ BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/
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);
@@ -1739,20 +2418,32 @@
* Now add the template and register the device unit.
*/
- memcpy(&btv->video_dev,&bttv_template,sizeof(bttv_template));
+ memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
+ memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
+ memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
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)
+
+ if(video_register_device(&btv->video_dev,VFL_TYPE_GRABBER)<0)
return -1;
+ if(video_register_device(&btv->vbi_dev,VFL_TYPE_VBI)<0)
+ {
+ video_unregister_device(&btv->video_dev);
+ return -1;
+ }
+ if (radio)
+ {
+ if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO)<0)
+ {
+ video_unregister_device(&btv->vbi_dev);
+ video_unregister_device(&btv->video_dev);
+ return -1;
+ }
+ }
+ i2c_register_bus(&btv->i2c);
+
return 0;
}
-
static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
{
u32 stat,astat;
@@ -1779,7 +2470,8 @@
if (astat&BT848_INT_FMTCHG)
{
IDEBUG(printk ("bttv: IRQ_FMTCHG\n"));
-/* btv->win.norm&=(dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */
+ /*btv->win.norm&=
+ (dstat&BT848_DSTATUS_NUML) ? (~1) : (~0); */
}
if (astat&BT848_INT_VPRES)
{
@@ -1795,22 +2487,47 @@
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); */
+
+ /* captured VBI frame */
if (stat&(1<<28))
{
btv->vbip=0;
wake_up_interruptible(&btv->vbiq);
}
+
+ /* captured full frame */
if (stat&(2<<28))
{
- bt848_set_risc_jmps(btv);
+ btv->grab++;
wake_up_interruptible(&btv->capq);
+ if ((--btv->grabbing))
+ {
+ btv->risc_jmp[5]=btv->gro;
+ btv->risc_jmp[11]=btv->gre;
+ bt848_set_geo(btv, btv->gwidth,
+ btv->gheight,
+ btv->gfmt);
+ } else {
+ bt848_set_risc_jmps(btv);
+ bt848_set_geo(btv, btv->win.width,
+ btv->win.height,
+ btv->win.color_fmt);
+ }
break;
}
+ if (stat&(8<<28))
+ {
+ btv->risc_jmp[5]=btv->gro;
+ btv->risc_jmp[11]=btv->gre;
+ btv->risc_jmp[12]=BT848_RISC_JUMP;
+ bt848_set_geo(btv, btv->gwidth, btv->gheight,
+ btv->gfmt);
+ }
}
if (astat&BT848_INT_OCERR)
{
@@ -1842,7 +2559,7 @@
}
if (astat&BT848_INT_HLOCK)
{
- if (dstat&BT848_DSTATUS_HLOC)
+ if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
audio(btv, AUDIO_ON);
else
audio(btv, AUDIO_OFF);
@@ -1858,12 +2575,14 @@
if (count > 20)
{
btwrite(0, BT848_INT_MASK);
- printk(KERN_ERR "bttv: IRQ lockup, cleared int mask\n");
+ printk(KERN_ERR
+ "bttv: IRQ lockup, cleared int mask\n");
}
}
}
+
/*
* Scan for a Bt848 card, request the irq and map the io memory
*/
@@ -1885,8 +2604,10 @@
}
for (pci_index = 0;
- !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
- pci_index, &bus, &devfn);
+ !pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849,
+ pci_index, &bus, &devfn)
+ ||!pcibios_find_device(PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
+ pci_index, &bus, &devfn);
++pci_index)
{
btv=&bttvs[bttv_num];
@@ -1899,8 +2620,13 @@
btv->vbi_even=NULL;
btv->vbiq=NULL;
btv->capq=NULL;
+ btv->capqo=NULL;
+ btv->capqe=NULL;
+
btv->vbip=VBIBUF_SIZE;
+ pcibios_read_config_word(btv->bus, btv->devfn, PCI_DEVICE_ID,
+ &btv->id);
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,
@@ -1921,7 +2647,8 @@
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(KERN_INFO "bttv: Brooktree Bt%d (rev %d) ",
+ btv->id, btv->revision);
printk("bus: %d, devfn: %d, ",
btv->bus, btv->devfn);
printk("irq: %d, ",btv->irq);
@@ -1959,8 +2686,8 @@
if (!latency)
{
latency=32;
- pcibios_write_config_byte(btv->bus, btv->devfn, PCI_LATENCY_TIMER,
- latency);
+ pcibios_write_config_byte(btv->bus, btv->devfn,
+ PCI_LATENCY_TIMER, latency);
}
DEBUG(printk(KERN_DEBUG "bttv: latency: %02x\n", latency));
bttv_num++;
@@ -1970,6 +2697,7 @@
return bttv_num;
}
+
static void release_bttv(void)
{
u8 command;
@@ -1979,21 +2707,28 @@
for (i=0;i<bttv_num; i++)
{
btv=&bttvs[i];
+
/* turn off all capturing, DMA and IRQs */
+ btand(~15, BT848_GPIO_DMA_CTL);
+
/* first disable interrupts before unmapping the memory! */
btwrite(0, BT848_INT_MASK);
btwrite(0xffffffffUL,BT848_INT_STAT);
btwrite(0x0, BT848_GPIO_OUT_EN);
- bt848_cap(btv, 0);
-
+ /* unregister i2c_bus */
+ i2c_unregister_bus((&btv->i2c));
+
/* 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->grisc)
+ kfree((void *) btv->grisc);
+
if (btv->risc_odd)
kfree((void *) btv->risc_odd);
@@ -2007,15 +2742,20 @@
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);
+ video_unregister_device(&btv->vbi_dev);
+ if (radio)
+ video_unregister_device(&btv->radio_dev);
}
}
-
#ifdef MODULE
int init_module(void)
@@ -2030,22 +2770,39 @@
if (find_bt848()<0)
return -EIO;
+ /* initialize Bt848s */
for (i=0; i<bttv_num; i++)
{
- if (init_bt848(&bttvs[i])<0)
+ if (init_bt848(i)<0)
{
release_bttv();
return -EIO;
}
- }
+ }
return 0;
}
+
+
#ifdef MODULE
void cleanup_module(void)
{
- release_bttv();
+ release_bttv();
}
#endif
+
+/*
+ * Local variables:
+ * c-indent-level: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov