blob: eb4328eb4bf94df1e23baa1875fa02b0faa9be00 [file] [log] [blame]
/*
bttv - Bt848 frame grabber driver
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de)
& Marcus Metzler (mocm@thp.uni-koeln.de)
(c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/version.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/signal.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <asm/pgtable.h>
#include <asm/page.h>
#include <linux/sched.h>
#include <linux/types.h>
#include <linux/wrapper.h>
#include <linux/interrupt.h>
#include <linux/kmod.h>
#include <linux/vmalloc.h>
#include <linux/init.h>
#include "bttvp.h"
#include "tuner.h"
#define DEBUG(x) /* Debug driver */
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)>(b))?(a):(b))
static void bt848_set_risc_jmps(struct bttv *btv, int state);
int bttv_num; /* number of Bt848s in use */
struct bttv bttvs[BTTV_MAX];
/* configuration variables */
#if defined(__sparc__) || defined(__powerpc__) || defined(__hppa__)
static unsigned int bigendian=1;
#else
static unsigned int bigendian=0;
#endif
static unsigned int radio[BTTV_MAX];
static unsigned int fieldnr = 0;
static unsigned int irq_debug = 0;
static unsigned int gbuffers = 2;
static unsigned int gbufsize = BTTV_MAX_FBUF;
static unsigned int combfilter = 0;
static unsigned int lumafilter = 0;
static int video_nr = -1;
static int radio_nr = -1;
static int vbi_nr = -1;
unsigned int bttv_debug = 0;
unsigned int bttv_verbose = 1;
unsigned int bttv_gpio = 0;
/* insmod options */
MODULE_PARM(radio,"1-4i");
MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)");
MODULE_PARM(bigendian,"i");
MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian");
MODULE_PARM(fieldnr,"i");
MODULE_PARM_DESC(fieldnr,"count fields, default is 0 (no)");
MODULE_PARM(bttv_verbose,"i");
MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)");
MODULE_PARM(bttv_gpio,"i");
MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)");
MODULE_PARM(bttv_debug,"i");
MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)");
MODULE_PARM(irq_debug,"i");
MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)");
MODULE_PARM(gbuffers,"i");
MODULE_PARM_DESC(gbuffers,"number of capture buffers, default is 2 (64 max)");
MODULE_PARM(gbufsize,"i");
MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000");
MODULE_PARM(combfilter,"i");
MODULE_PARM(lumafilter,"i");
MODULE_PARM(video_nr,"i");
MODULE_PARM(radio_nr,"i");
MODULE_PARM(vbi_nr,"i");
MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards");
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr");
MODULE_LICENSE("GPL");
/* kernel args */
#ifndef MODULE
static int __init p_radio(char *str) { return bttv_parse(str,BTTV_MAX,radio); }
__setup("bttv.radio=", p_radio);
#endif
#define I2C_TIMING (0x7<<4)
#define I2C_DELAY 10
#define I2C_SET(CTRL,DATA) \
{ btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); }
#define I2C_GET() (btread(BT848_I2C)&1)
#define BURSTOFFSET 76
#define BTTV_ERRORS 5
/*******************************/
/* Memory management functions */
/*******************************/
#define MDEBUG(x) do { } while(0) /* Debug memory management */
/* [DaveM] I've recoded most of this so that:
* 1) It's easier to tell what is happening
* 2) It's more portable, especially for translating things
* out of vmalloc mapped areas in the kernel.
* 3) Less unnecessary translations happen.
*
* The code used to assume that the kernel vmalloc mappings
* existed in the page tables of every process, this is simply
* not guarenteed. We now use pgd_offset_k which is the
* defined way to get at the kernel page tables.
*/
/* Given PGD from the address space's page table, return the kernel
* virtual mapping of the physical memory mapped at ADR.
*/
static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr)
{
unsigned long ret = 0UL;
pmd_t *pmd;
pte_t *ptep, pte;
if (!pgd_none(*pgd)) {
pmd = pmd_offset(pgd, adr);
if (!pmd_none(*pmd)) {
ptep = pte_offset(pmd, adr);
pte = *ptep;
if(pte_present(pte)) {
ret = (unsigned long) page_address(pte_page(pte));
ret |= (adr & (PAGE_SIZE - 1));
}
}
}
MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret));
return ret;
}
static inline unsigned long uvirt_to_bus(unsigned long adr)
{
unsigned long kva, ret;
kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr);
ret = virt_to_bus((void *)kva);
MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret));
return ret;
}
static inline unsigned long kvirt_to_bus(unsigned long adr)
{
unsigned long va, kva, ret;
va = VMALLOC_VMADDR(adr);
kva = uvirt_to_kva(pgd_offset_k(va), va);
ret = virt_to_bus((void *)kva);
MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret));
return ret;
}
/* Here we want the physical address of the memory.
* This is used when initializing the contents of the
* area and marking the pages as reserved.
*/
static inline unsigned long kvirt_to_pa(unsigned long adr)
{
unsigned long va, kva, ret;
va = VMALLOC_VMADDR(adr);
kva = uvirt_to_kva(pgd_offset_k(va), va);
ret = __pa(kva);
MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret));
return ret;
}
static void * rvmalloc(signed long size)
{
void * mem;
unsigned long adr, page;
mem=vmalloc_32(size);
if (NULL == mem)
printk(KERN_INFO "bttv: vmalloc_32(%ld) failed\n",size);
else {
/* Clear the ram out, no junk to the user */
memset(mem, 0, size);
adr=(unsigned long) mem;
while (size > 0)
{
page = kvirt_to_pa(adr);
mem_map_reserve(virt_to_page(__va(page)));
adr+=PAGE_SIZE;
size-=PAGE_SIZE;
}
}
return mem;
}
static void rvfree(void * mem, signed long size)
{
unsigned long adr, page;
if (mem)
{
adr=(unsigned long) mem;
while (size > 0)
{
page = kvirt_to_pa(adr);
mem_map_unreserve(virt_to_page(__va(page)));
adr+=PAGE_SIZE;
size-=PAGE_SIZE;
}
vfree(mem);
}
}
/*
* 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(gbuffers*gbufsize);
else
printk(KERN_ERR "bttv%d: Double alloc of fbuffer!\n",
btv->nr);
if(!btv->fbuffer)
return -ENOBUFS;
return 0;
}
/* ----------------------------------------------------------------------- */
void bttv_gpio_tracking(struct bttv *btv, char *comment)
{
unsigned int outbits, data;
outbits = btread(BT848_GPIO_OUT_EN);
data = btread(BT848_GPIO_DATA);
printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n",
btv->nr,outbits,data & outbits, data & ~outbits, comment);
}
static char *audio_modes[] = { "audio: tuner", "audio: radio", "audio: extern",
"audio: intern", "audio: off" };
static void audio(struct bttv *btv, int mode, int no_irq_context)
{
btaor(bttv_tvcards[btv->type].gpiomask, ~bttv_tvcards[btv->type].gpiomask,
BT848_GPIO_OUT_EN);
switch (mode)
{
case AUDIO_MUTE:
btv->audio|=AUDIO_MUTE;
break;
case AUDIO_UNMUTE:
btv->audio&=~AUDIO_MUTE;
mode=btv->audio;
break;
case AUDIO_OFF:
mode=AUDIO_OFF;
break;
case AUDIO_ON:
mode=btv->audio;
break;
default:
btv->audio&=AUDIO_MUTE;
btv->audio|=mode;
break;
}
/* if audio mute or not in H-lock, turn audio off */
if ((btv->audio&AUDIO_MUTE))
mode=AUDIO_OFF;
if ((mode == AUDIO_TUNER) && (btv->radio))
mode = AUDIO_RADIO;
btaor(bttv_tvcards[btv->type].audiomux[mode],
~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
if (bttv_gpio)
bttv_gpio_tracking(btv,audio_modes[mode]);
if (no_irq_context)
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode));
}
extern inline void bt848_dma(struct bttv *btv, uint state)
{
if (state)
btor(3, BT848_GPIO_DMA_CTL);
else
btand(~3, BT848_GPIO_DMA_CTL);
}
/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/
/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C
PLL_X = Reference pre-divider (0=1, 1=2)
PLL_C = Post divider (0=6, 1=4)
PLL_I = Integer input
PLL_F = Fractional input
F_input = 28.636363 MHz:
PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0
*/
static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout)
{
unsigned char fl, fh, fi;
/* prevent overflows */
fin/=4;
fout/=4;
fout*=12;
fi=fout/fin;
fout=(fout%fin)*256;
fh=fout/fin;
fout=(fout%fin)*256;
fl=fout/fin;
/*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/
btwrite(fl, BT848_PLL_F_LO);
btwrite(fh, BT848_PLL_F_HI);
btwrite(fi|BT848_PLL_X, BT848_PLL_XCI);
}
static int set_pll(struct bttv *btv)
{
int i;
unsigned long tv;
if (!btv->pll.pll_crystal)
return 0;
if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) {
/* no PLL needed */
if (btv->pll.pll_current == 0) {
/* printk ("bttv%d: PLL: is off\n",btv->nr); */
return 0;
}
if (bttv_verbose)
printk ("bttv%d: PLL: switching off\n",btv->nr);
btwrite(0x00,BT848_TGCTRL);
btwrite(0x00,BT848_PLL_XCI);
btv->pll.pll_current = 0;
return 0;
}
if (btv->pll.pll_ofreq == btv->pll.pll_current) {
/* printk("bttv%d: PLL: no change required\n",btv->nr); */
return 1;
}
if (bttv_verbose)
printk("bttv%d: PLL: %d => %d ... ",btv->nr,
btv->pll.pll_ifreq, btv->pll.pll_ofreq);
set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq);
/* Let other people run while the PLL stabilizes */
tv=jiffies+HZ/10; /* .1 seconds */
do
{
schedule();
}
while(time_before(jiffies,tv));
for (i=0; i<100; i++)
{
if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK))
btwrite(0,BT848_DSTATUS);
else
{
btwrite(0x08,BT848_TGCTRL);
btv->pll.pll_current = btv->pll.pll_ofreq;
if (bttv_verbose)
printk("ok\n");
return 1;
}
mdelay(10);
}
btv->pll.pll_current = 0;
if (bttv_verbose)
printk("oops\n");
return -1;
}
static void bt848_muxsel(struct bttv *btv, unsigned int input)
{
#if 0 /* seems no card uses this ... */
btaor(bttv_tvcards[btv->type].gpiomask2,~bttv_tvcards[btv->type].gpiomask2,
BT848_GPIO_OUT_EN);
#endif
/* This seems to get rid of some synchronization problems */
btand(~(3<<5), BT848_IFORM);
mdelay(10);
input %= bttv_tvcards[btv->type].video_inputs;
if (input==bttv_tvcards[btv->type].svhs)
{
btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
}
else
{
btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
}
btaor((bttv_tvcards[btv->type].muxsel[input&7]&3)<<5, ~(3<<5), BT848_IFORM);
audio(btv, (input!=bttv_tvcards[btv->type].tuner) ?
AUDIO_EXTERN : AUDIO_TUNER, 1);
#if 0 /* seems no card uses this ... */
btaor(bttv_tvcards[btv->type].muxsel[input]>>4,
~bttv_tvcards[btv->type].gpiomask2, BT848_GPIO_DATA);
if (bttv_gpio)
bttv_gpio_tracking(btv,"muxsel");
#endif
}
struct tvnorm
{
u32 Fsc;
u16 swidth, sheight; /* scaled standard width, height */
u16 totalwidth;
u8 adelay, bdelay, iform;
u32 scaledtwidth;
u16 hdelayx1, hactivex1;
u16 vdelay;
u8 vbipack;
};
static struct tvnorm tvnorms[] = {
/* PAL-BDGHI */
/* max. active video is actually 922, but 924 is divisible by 4 and 3! */
/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */
{ 35468950,
924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1),
1135, 186, 924,
#ifdef VIDEODAT_HACK
VBI_MAXLINES*2,
#else
0x20,
#endif
255},
/* NTSC */
{ 28636363,
768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0),
910, 128, 910, 0x1a, 144},
#if 0
/* SECAM EAST */
{ 35468950,
768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
944, 186, 922, 0x20, 255},
#else
/* SECAM L */
{ 35468950,
924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1),
1135, 186, 922, 0x20, 255},
#endif
/* PAL-NC */
{ 28636363,
640, 576, 910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0),
780, 130, 734, 0x1a, 144},
/* PAL-M */
{ 28636363,
640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0),
780, 135, 754, 0x1a, 144},
/* PAL-N */
{ 35468950,
768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1),
944, 186, 922, 0x20, 144},
/* NTSC-Japan */
{ 28636363,
640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0),
780, 135, 754, 0x16, 144},
};
#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm))
#define VBI_SPL 2044
/* RISC command to write one VBI data line */
#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL
static void make_vbitab(struct bttv *btv)
{
int i;
unsigned int *po=(unsigned int *) btv->vbi_odd;
unsigned int *pe=(unsigned int *) btv->vbi_even;
if (bttv_debug > 1)
printk("bttv%d: vbi1: po=%08lx pe=%08lx\n",
btv->nr,virt_to_bus(po), virt_to_bus(pe));
*(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0;
for (i=0; i<VBI_MAXLINES; i++)
{
*(po++)=cpu_to_le32(VBI_RISC);
*(po++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048));
}
*(po++)=cpu_to_le32(BT848_RISC_JUMP);
*(po++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+4));
*(pe++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(pe++)=0;
for (i=VBI_MAXLINES; i<VBI_MAXLINES*2; i++)
{
*(pe++)=cpu_to_le32(VBI_RISC);
*(pe++)=cpu_to_le32(kvirt_to_bus((unsigned long)btv->vbibuf+i*2048));
}
*(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16));
*(pe++)=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
if (bttv_debug > 1)
printk("bttv%d: vbi2: po=%08lx pe=%08lx\n",
btv->nr,virt_to_bus(po), virt_to_bus(pe));
}
static int fmtbppx2[16] = {
8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0
};
static int palette2fmt[] = {
0,
BT848_COLOR_FMT_Y8,
BT848_COLOR_FMT_RGB8,
BT848_COLOR_FMT_RGB16,
BT848_COLOR_FMT_RGB24,
BT848_COLOR_FMT_RGB32,
BT848_COLOR_FMT_RGB15,
BT848_COLOR_FMT_YUY2,
BT848_COLOR_FMT_YUY2,
-1,
-1,
-1,
BT848_COLOR_FMT_RAW,
BT848_COLOR_FMT_YCrCb422,
BT848_COLOR_FMT_YCrCb411,
BT848_COLOR_FMT_YCrCb422,
BT848_COLOR_FMT_YCrCb411,
};
#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int))
static int make_rawrisctab(struct bttv *btv, unsigned int *ro,
unsigned int *re, unsigned int *vbuf)
{
unsigned long line;
unsigned long bpl=1024; /* bytes per line */
unsigned long vadr=(unsigned long) vbuf;
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
*(ro++)=cpu_to_le32(0);
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
*(re++)=cpu_to_le32(0);
/* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY
is 2 and without separate VBI grabbing.
We'll have to handle this inside the IRQ handler ... */
for (line=0; line < 640; line++)
{
*(ro++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
*(ro++)=cpu_to_le32(kvirt_to_bus(vadr));
*(re++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL);
*(re++)=cpu_to_le32(kvirt_to_bus(vadr+gbufsize/2));
vadr+=bpl;
}
*(ro++)=cpu_to_le32(BT848_RISC_JUMP);
*(ro++)=cpu_to_le32(btv->bus_vbi_even);
*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
*(re++)=cpu_to_le32(btv->bus_vbi_odd);
return 0;
}
static int make_prisctab(struct bttv *btv, unsigned int *ro,
unsigned int *re,
unsigned int *vbuf, unsigned short width,
unsigned short height, unsigned short fmt)
{
unsigned long line, lmask;
unsigned long bl, blcr, blcb, rcmd;
unsigned long todo;
unsigned int **rp;
int inter;
unsigned long cbadr, cradr;
unsigned long vadr=(unsigned long) vbuf;
int shift, csize;
if (bttv_debug > 1)
printk("bttv%d: prisc1: ro=%08lx re=%08lx\n",
btv->nr,virt_to_bus(ro), virt_to_bus(re));
switch(fmt)
{
case VIDEO_PALETTE_YUV422P:
csize=(width*height)>>1;
shift=1;
lmask=0;
break;
case VIDEO_PALETTE_YUV411P:
csize=(width*height)>>2;
shift=2;
lmask=0;
break;
case VIDEO_PALETTE_YUV420P:
csize=(width*height)>>2;
shift=1;
lmask=1;
break;
case VIDEO_PALETTE_YUV410P:
csize=(width*height)>>4;
shift=2;
lmask=3;
break;
default:
return -1;
}
cbadr=vadr+(width*height);
cradr=cbadr+csize;
inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
*(ro++)=0;
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3);
*(re++)=0;
for (line=0; line < (height<<(1^inter)); line++)
{
if(line==height)
{
vadr+=csize<<1;
cbadr=vadr+(width*height);
cradr=cbadr+csize;
}
if (inter)
rp= (line&1) ? &re : &ro;
else
rp= (line>=height) ? &ro : &re;
if(line&lmask)
rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL;
else
rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL;
todo=width;
while(todo)
{
bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<<shift;
blcb=(PAGE_SIZE-((PAGE_SIZE-1)&cbadr))<<shift;
bl=(blcr<bl) ? blcr : bl;
bl=(blcb<bl) ? blcb : bl;
bl=(bl>todo) ? todo : bl;
blcr=bl>>shift;
blcb=blcr;
/* bl now containts the longest row that can be written */
todo-=bl;
if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */
*((*rp)++)=cpu_to_le32(rcmd|bl);
*((*rp)++)=cpu_to_le32(blcb|(blcr<<16));
*((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
vadr+=bl;
if((rcmd&(15<<28))==BT848_RISC_WRITE123)
{
*((*rp)++)=cpu_to_le32(kvirt_to_bus(cbadr));
cbadr+=blcb;
*((*rp)++)=cpu_to_le32(kvirt_to_bus(cradr));
cradr+=blcr;
}
rcmd&=~BT848_RISC_SOL; /* only the first has SOL */
}
}
*(ro++)=cpu_to_le32(BT848_RISC_JUMP);
*(ro++)=cpu_to_le32(btv->bus_vbi_even);
*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
*(re++)=cpu_to_le32(btv->bus_vbi_odd);
if (bttv_debug > 1)
printk("bttv%d: prisc2: ro=%08lx re=%08lx\n",
btv->nr,virt_to_bus(ro), virt_to_bus(re));
return 0;
}
static int make_vrisctab(struct bttv *btv, unsigned int *ro,
unsigned int *re,
unsigned int *vbuf, unsigned short width,
unsigned short height, unsigned short palette)
{
unsigned long line;
unsigned long bpl; /* bytes per line */
unsigned long bl;
unsigned long todo;
unsigned int **rp;
int inter;
unsigned long vadr=(unsigned long) vbuf;
if (palette==VIDEO_PALETTE_RAW)
return make_rawrisctab(btv, ro, re, vbuf);
if (palette>=VIDEO_PALETTE_PLANAR)
return make_prisctab(btv, ro, re, vbuf, width, height, palette);
if (bttv_debug > 1)
printk("bttv%d: vrisc1: ro=%08lx re=%08lx\n",
btv->nr,virt_to_bus(ro), virt_to_bus(re));
inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0;
bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2;
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
*(ro++)=cpu_to_le32(0);
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
*(re++)=cpu_to_le32(0);
for (line=0; line < (height<<(1^inter)); line++)
{
if (inter)
rp= (line&1) ? &re : &ro;
else
rp= (line>=height) ? &ro : &re;
bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr);
if (bpl<=bl)
{
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|
BT848_RISC_EOL|bpl);
*((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
vadr+=bpl;
}
else
{
todo=bpl;
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL|bl);
*((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
vadr+=bl;
todo-=bl;
while (todo>PAGE_SIZE)
{
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|PAGE_SIZE);
*((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
vadr+=PAGE_SIZE;
todo-=PAGE_SIZE;
}
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL|todo);
*((*rp)++)=cpu_to_le32(kvirt_to_bus(vadr));
vadr+=todo;
}
}
*(ro++)=cpu_to_le32(BT848_RISC_JUMP);
*(ro++)=cpu_to_le32(btv->bus_vbi_even);
*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16));
*(re++)=cpu_to_le32(btv->bus_vbi_odd);
if (bttv_debug > 1)
printk("bttv%d: vrisc2: ro=%08lx re=%08lx\n",
btv->nr,virt_to_bus(ro), virt_to_bus(re));
return 0;
}
static unsigned char lmaskt[8] =
{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80};
static unsigned char rmaskt[8] =
{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h)
{
unsigned char lmask, rmask, *p;
int W, l, r;
int i;
if (bttv_debug > 1)
printk("bttv clip: %dx%d+%d+%d\n",w,h,x,y);
/* bitmap is fixed width, 128 bytes (1024 pixels represented) */
if (x<0)
{
w+=x;
x=0;
}
if (y<0)
{
h+=y;
y=0;
}
if (w < 0 || h < 0) /* catch bad clips */
return;
/* out of range data should just fall through */
if (y+h>=625)
h=625-y;
if (x+w>=1024)
w=1024-x;
l=x>>3;
r=(x+w-1)>>3;
W=r-l-1;
lmask=lmaskt[x&7];
rmask=rmaskt[(x+w-1)&7];
p=clipmap+128*y+l;
if (W>0)
{
for (i=0; i<h; i++, p+=128)
{
*p|=lmask;
memset(p+1, 0xff, W);
p[W+1]|=rmask;
}
} else if (!W) {
for (i=0; i<h; i++, p+=128)
{
p[0]|=lmask;
p[1]|=rmask;
}
} else {
for (i=0; i<h; i++, p+=128)
p[0]|=lmask&rmask;
}
}
static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr)
{
int i, line, x, y, bpl, width, height, inter, maxw;
unsigned int bpp, dx, sx, **rp, *ro, *re, flags, len;
unsigned long adr;
unsigned char *clipmap, *clipline, cbit, lastbit, outofmem;
/* take care: bpp != btv->win.bpp is allowed here */
bpp = fmtbppx2[btv->win.color_fmt&0xf]/2;
bpl=btv->win.bpl;
adr=btv->win.vidadr + btv->win.x * btv->win.bpp + btv->win.y * bpl;
inter=(btv->win.interlace&1)^1;
width=btv->win.width;
height=btv->win.height;
if (bttv_debug > 1)
printk("bttv%d: clip1: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
btv->nr,btv->picture.palette,width,height,bpl,bpp);
if(width > 1023)
width = 1023; /* sanity check */
if(height > 625)
height = 625; /* sanity check */
ro=btv->risc_scr_odd;
re=btv->risc_scr_even;
if (bttv_debug)
printk("bttv%d: clip: ro=%08lx re=%08lx\n",
btv->nr,virt_to_bus(ro), virt_to_bus(re));
if ((clipmap=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
/* can't clip, don't generate any risc code */
*(ro++)=cpu_to_le32(BT848_RISC_JUMP);
*(ro++)=cpu_to_le32(btv->bus_vbi_even);
*(re++)=cpu_to_le32(BT848_RISC_JUMP);
*(re++)=cpu_to_le32(btv->bus_vbi_odd);
}
if (ncr < 0) { /* bitmap was pased */
memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE);
} else { /* convert rectangular clips to a bitmap */
memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */
for (i=0; i<ncr; i++)
clip_draw_rectangle(clipmap, cr[i].x, cr[i].y,
cr[i].width, cr[i].height);
}
/* clip against viewing window AND screen
so we do not have to rely on the user program
*/
maxw = (bpl - btv->win.x * btv->win.bpp) / bpp;
clip_draw_rectangle(clipmap, (width > maxw) ? maxw : width,
0, 1024, 768);
clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ?
(btv->win.sheight-btv->win.y) : height,1024,768);
if (btv->win.x<0)
clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768);
if (btv->win.y<0)
clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y));
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
*(ro++)=cpu_to_le32(0);
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1);
*(re++)=cpu_to_le32(0);
/* translate bitmap to risc code */
for (line=outofmem=0; line < (height<<inter) && !outofmem; line++)
{
y = line>>inter;
rp= (line&1) ? &re : &ro;
clipline = clipmap + (y<<7); /* running pointers ... */
lastbit = *clipline & 1;
for(x=dx=0,sx=0; x<=width && !outofmem;) {
if (0 == (x&7)) {
/* check bytes not bits if we can ... */
if (lastbit) {
while (0xff==*clipline && x<width-8) {
x += 8;
dx += 8;
clipline++;
}
} else {
while (0x00==*clipline && x<width-8) {
x += 8;
dx += 8;
clipline++;
}
}
}
cbit = *clipline & (1<<(x&7));
if (x < width && !lastbit == !cbit) {
dx++;
} else {
/* generate the dma controller code */
len = dx * bpp;
flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0);
flags |= ((!sx) ? BT848_RISC_SOL : 0);
flags |= ((sx + dx == width) ? BT848_RISC_EOL : 0);
if (!lastbit) {
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|flags|len);
*((*rp)++)=cpu_to_le32(adr + bpp * sx);
} else {
*((*rp)++)=cpu_to_le32(BT848_RISC_SKIP|flags|len);
}
lastbit=cbit;
sx += dx;
dx = 1;
if (ro - btv->risc_scr_odd>(RISCMEM_LEN>>3) - 16)
outofmem++;
if (re - btv->risc_scr_even>(RISCMEM_LEN>>3) - 16)
outofmem++;
}
x++;
if (0 == (x&7))
clipline++;
}
if ((!inter)||(line&1))
adr+=bpl;
}
vfree(clipmap);
/* outofmem flag relies on the following code to discard extra data */
*(ro++)=cpu_to_le32(BT848_RISC_JUMP);
*(ro++)=cpu_to_le32(btv->bus_vbi_even);
*(re++)=cpu_to_le32(BT848_RISC_JUMP);
*(re++)=cpu_to_le32(btv->bus_vbi_odd);
if (bttv_debug > 1)
printk("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n",
btv->nr,btv->picture.palette,width,height,bpl,bpp);
}
/*
* Set the registers for the size we have specified. Don't bother
* trying to understand this without the BT848 manual in front of
* you [AC].
*
* PS: The manual is free for download in .pdf format from
* www.brooktree.com - nicely done those folks.
*/
static inline void bt848_set_eogeo(struct bttv *btv, struct tvnorm *tvn,
int odd, int width, int height)
{
u16 vscale, hscale;
u32 xsf, sr;
u16 hdelay;
u8 crop, vtc;
int inter = (height>tvn->sheight/2) ? 0 : 1;
int off = odd ? 0x80 : 0x00;
xsf = (width*tvn->scaledtwidth)/tvn->swidth;
hscale = ((tvn->totalwidth*4096UL)/xsf-4096);
hdelay = tvn->hdelayx1;
hdelay = (hdelay*width)/tvn->swidth;
hdelay &= 0x3fe;
sr=((tvn->sheight>>inter)*512)/height-512;
vscale=(0x10000UL-sr)&0x1fff;
crop=((width>>8)&0x03)|((hdelay>>6)&0x0c)|
((tvn->sheight>>4)&0x30)|((tvn->vdelay>>2)&0xc0);
vscale |= inter ? (BT848_VSCALE_INT<<8) : 0;
if (combfilter) {
/* Some people say interpolation looks bad ... */
vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0);
if (width < 769)
btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
else
btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
} else {
vtc = 0;
btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off);
}
btwrite(vtc, BT848_E_VTC+off);
btwrite(hscale>>8, BT848_E_HSCALE_HI+off);
btwrite(hscale&0xff, BT848_E_HSCALE_LO+off);
btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off);
btwrite(vscale&0xff, BT848_E_VSCALE_LO+off);
btwrite(width&0xff, BT848_E_HACTIVE_LO+off);
btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off);
btwrite(tvn->sheight&0xff, BT848_E_VACTIVE_LO+off);
btwrite(tvn->vdelay&0xff, BT848_E_VDELAY_LO+off);
btwrite(crop, BT848_E_CROP+off);
}
static void bt848_set_geo(struct bttv *btv,
int no_irq_context)
{
u16 ewidth, eheight, owidth, oheight;
u16 format, bswap;
struct tvnorm *tvn;
tvn=&tvnorms[btv->win.norm];
btwrite(tvn->adelay, BT848_ADELAY);
btwrite(tvn->bdelay, BT848_BDELAY);
btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM);
btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE);
btwrite(1, BT848_VBI_PACK_DEL);
btv->pll.pll_ofreq = tvn->Fsc;
if (no_irq_context)
set_pll(btv);
btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0;
if (0 == btv->risc_cap_odd &&
0 == btv->risc_cap_even) {
/* overlay only */
owidth = btv->win.width;
oheight = btv->win.height;
ewidth = btv->win.width;
eheight = btv->win.height;
format = btv->win.color_fmt;
bswap = btv->fb_color_ctl;
} else if (-1 != btv->gq_grab &&
0 == btv->risc_cap_odd &&
!btv->win.interlace &&
btv->scr_on) {
/* odd field -> overlay, even field -> capture */
owidth = btv->win.width;
oheight = btv->win.height;
ewidth = btv->gbuf[btv->gq_grab].width;
eheight = btv->gbuf[btv->gq_grab].height;
format = (btv->win.color_fmt & 0xf0) |
(btv->gbuf[btv->gq_grab].fmt & 0x0f);
bswap = btv->fb_color_ctl & 0x0a;
} else {
/* capture only */
owidth = btv->gbuf[btv->gq_grab].width;
oheight = btv->gbuf[btv->gq_grab].height;
ewidth = btv->gbuf[btv->gq_grab].width;
eheight = btv->gbuf[btv->gq_grab].height;
format = btv->gbuf[btv->gq_grab].fmt;
bswap = 0;
}
/* program odd + even fields */
bt848_set_eogeo(btv, tvn, 1, owidth, oheight);
bt848_set_eogeo(btv, tvn, 0, ewidth, eheight);
btwrite(format, BT848_COLOR_FMT);
btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL);
}
static int bpp2fmt[4] = {
BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16,
BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32
};
static void bt848_set_winsize(struct bttv *btv)
{
unsigned short format;
if (btv->picture.palette > 0 && btv->picture.palette <= VIDEO_PALETTE_YUV422) {
/* format set by VIDIOCSPICT */
format = palette2fmt[btv->picture.palette];
} else {
/* use default for the given color depth */
format = (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 :
bpp2fmt[(btv->win.bpp-1)&3];
}
btv->win.color_fmt = format;
if (bigendian &&
format == BT848_COLOR_FMT_RGB32) {
btv->fb_color_ctl =
BT848_COLOR_CTL_WSWAP_ODD |
BT848_COLOR_CTL_WSWAP_EVEN |
BT848_COLOR_CTL_BSWAP_ODD |
BT848_COLOR_CTL_BSWAP_EVEN;
} else if (bigendian &&
(format == BT848_COLOR_FMT_RGB16 ||
format == BT848_COLOR_FMT_RGB15)) {
btv->fb_color_ctl =
BT848_COLOR_CTL_BSWAP_ODD |
BT848_COLOR_CTL_BSWAP_EVEN;
} else {
btv->fb_color_ctl = 0;
}
/* RGB8 seems to be a 9x5x5 GRB color cube starting at
* color 16. Why the h... can't they even mention this in the
* data sheet? [AC - because it's a standard format so I guess
* it never occurred to them]
* Enable dithering in this mode.
*/
if (format==BT848_COLOR_FMT_RGB8)
btand(~BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL);
else
btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL);
bt848_set_geo(btv,1);
}
/*
* Grab into virtual memory.
*/
static int vgrab(struct bttv *btv, struct video_mmap *mp)
{
unsigned int *ro, *re;
unsigned int *vbuf;
unsigned long flags;
if(btv->fbuffer==NULL)
{
if(fbuffer_alloc(btv))
return -ENOBUFS;
}
if(mp->frame >= gbuffers || mp->frame < 0)
return -EINVAL;
if(btv->gbuf[mp->frame].stat != GBUFFER_UNUSED)
return -EBUSY;
if(mp->height < 32 || mp->width < 48)
return -EINVAL;
if (mp->format >= PALETTEFMT_MAX)
return -EINVAL;
if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2
> gbufsize)
return -EINVAL;
if(-1 == palette2fmt[mp->format])
return -EINVAL;
/*
* Ok load up the BT848
*/
vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame);
ro=btv->gbuf[mp->frame].risc;
re=ro+2048;
make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format);
if (bttv_debug)
printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n",
btv->nr,mp->frame,mp->format,mp->width,mp->height);
spin_lock_irqsave(&btv->s_lock, flags);
btv->gbuf[mp->frame].stat = GBUFFER_GRABBING;
btv->gbuf[mp->frame].fmt = palette2fmt[mp->format];
btv->gbuf[mp->frame].width = mp->width;
btv->gbuf[mp->frame].height = mp->height;
btv->gbuf[mp->frame].ro = virt_to_bus(ro);
btv->gbuf[mp->frame].re = virt_to_bus(re);
#if 1
if (mp->height <= tvnorms[btv->win.norm].sheight/2 &&
mp->format != VIDEO_PALETTE_RAW)
btv->gbuf[mp->frame].ro = 0;
#endif
if (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) {
btv->gq_start = 1;
btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
}
btv->gqueue[btv->gq_in++] = mp->frame;
btv->gq_in = btv->gq_in % MAX_GBUFFERS;
btor(3, BT848_CAP_CTL);
btor(3, BT848_GPIO_DMA_CTL);
spin_unlock_irqrestore(&btv->s_lock, flags);
return 0;
}
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;
DECLARE_WAITQUEUE(wait, current);
/* BROKEN: RETURNS VBI WHEN IT SHOULD RETURN GRABBED VIDEO FRAME */
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;
add_wait_queue(&btv->vbiq, &wait);
current->state = TASK_INTERRUPTIBLE;
if (todo && q==VBIBUF_SIZE-btv->vbip)
{
if(nonblock)
{
remove_wait_queue(&btv->vbiq, &wait);
current->state = TASK_RUNNING;
if(count==todo)
return -EWOULDBLOCK;
return count-todo;
}
schedule();
if(signal_pending(current))
{
remove_wait_queue(&btv->vbiq, &wait);
current->state = TASK_RUNNING;
if(todo==count)
return -EINTR;
else
return count-todo;
}
}
remove_wait_queue(&btv->vbiq, &wait);
current->state = TASK_RUNNING;
}
if (todo)
{
if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
return -EFAULT;
btv->vbip+=todo;
}
return count;
}
static inline void burst(int on)
{
tvnorms[0].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0);
tvnorms[0].hdelayx1 = 186 - (on?BURSTOFFSET :0);
tvnorms[2].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0);
tvnorms[2].hdelayx1 = 186 - (on?BURSTOFFSET :0);
}
/*
* called from irq handler on fatal errors. Takes the grabber chip
* offline, flag it needs a reinitialization (which can't be done
* from irq context) and wake up all sleeping proccesses. They would
* block forever else. We also need someone who actually does the
* reinitialization from process context...
*/
static void bt848_offline(struct bttv *btv)
{
int i;
spin_lock(&btv->s_lock);
/* cancel all outstanding grab requests */
btv->gq_in = 0;
btv->gq_out = 0;
btv->gq_grab = -1;
for (i = 0; i < gbuffers; i++)
if (btv->gbuf[i].stat == GBUFFER_GRABBING)
btv->gbuf[i].stat = GBUFFER_ERROR;
/* disable screen overlay and DMA */
btv->risc_cap_odd = 0;
btv->risc_cap_even = 0;
bt848_set_risc_jmps(btv,0);
/* flag the chip needs a restart */
btv->needs_restart = 1;
spin_unlock(&btv->s_lock);
wake_up_interruptible(&btv->vbiq);
wake_up_interruptible(&btv->capq);
}
static void bt848_restart(struct bttv *btv)
{
unsigned long irq_flags;
if (bttv_verbose)
printk("bttv%d: resetting chip\n",btv->nr);
btwrite(0xfffffUL, BT848_INT_STAT);
btand(~15, BT848_GPIO_DMA_CTL);
btwrite(0, BT848_SRESET);
btwrite(virt_to_bus(btv->risc_jmp+2),
BT848_RISC_STRT_ADD);
/* enforce pll reprogramming */
btv->pll.pll_current = 0;
set_pll(btv);
spin_lock_irqsave(&btv->s_lock, irq_flags);
btv->errors = 0;
btv->needs_restart = 0;
bt848_set_geo(btv,0);
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
}
/*
* 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 i,ret;
ret = -EBUSY;
if (bttv_debug)
printk("bttv%d: open called\n",btv->nr);
down(&btv->lock);
if (btv->user)
goto out_unlock;
btv->fbuffer=(unsigned char *) rvmalloc(gbuffers*gbufsize);
ret = -ENOMEM;
if (!btv->fbuffer)
goto out_unlock;
btv->gq_in = 0;
btv->gq_out = 0;
btv->gq_grab = -1;
for (i = 0; i < gbuffers; i++)
btv->gbuf[i].stat = GBUFFER_UNUSED;
if (btv->needs_restart)
bt848_restart(btv);
burst(0);
set_pll(btv);
btv->user++;
up(&btv->lock);
return 0;
out_unlock:
up(&btv->lock);
return ret;
}
static void bttv_close(struct video_device *dev)
{
struct bttv *btv=(struct bttv *)dev;
unsigned long irq_flags;
int need_wait;
down(&btv->lock);
btv->user--;
spin_lock_irqsave(&btv->s_lock, irq_flags);
need_wait = (-1 != btv->gq_grab);
btv->gq_start = 0;
btv->gq_in = 0;
btv->gq_out = 0;
btv->gq_grab = -1;
btv->scr_on = 0;
btv->risc_cap_odd = 0;
btv->risc_cap_even = 0;
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
/*
* A word of warning. At this point the chip
* is still capturing because its FIFO hasn't emptied
* and the DMA control operations are posted PCI
* operations.
*/
btread(BT848_I2C); /* This fixes the PCI posting delay */
if (need_wait) {
/*
* This is sucky but right now I can't find a good way to
* be sure its safe to free the buffer. We wait 5-6 fields
* which is more than sufficient to be sure.
*/
current->state = TASK_UNINTERRUPTIBLE;
schedule_timeout(HZ/10); /* Wait 1/10th of a second */
}
/*
* We have allowed it to drain.
*/
if(btv->fbuffer)
rvfree((void *) btv->fbuffer, gbuffers*gbufsize);
btv->fbuffer=0;
up(&btv->lock);
}
/***********************************/
/* 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, unsigned long data)
{
u32 datahi;
datahi=(data>>7)&2;
btwrite(data&0xff, BT848_SAT_U_LO);
btaor(datahi, ~2, BT848_E_CONTROL);
btaor(datahi, ~2, BT848_O_CONTROL);
}
static inline void bt848_sat_v(struct bttv *btv, unsigned long data)
{
u32 datahi;
datahi=(data>>8)&1;
btwrite(data&0xff, BT848_SAT_V_LO);
btaor(datahi, ~1, BT848_E_CONTROL);
btaor(datahi, ~1, BT848_O_CONTROL);
}
/*
* ioctl routine
*/
static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
struct bttv *btv=(struct bttv *)dev;
unsigned long irq_flags;
int i,ret = 0;
if (bttv_debug > 1)
printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd);
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability b;
strcpy(b.name,btv->video_dev.name);
b.type = VID_TYPE_CAPTURE|
((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) |
VID_TYPE_OVERLAY|
VID_TYPE_CLIPPING|
VID_TYPE_FRAMERAM|
VID_TYPE_SCALES;
b.channels = bttv_tvcards[btv->type].video_inputs;
b.audios = bttv_tvcards[btv->type].audio_inputs;
b.maxwidth = tvnorms[btv->win.norm].swidth;
b.maxheight = tvnorms[btv->win.norm].sheight;
b.minwidth = 48;
b.minheight = 32;
if(copy_to_user(arg,&b,sizeof(b)))
return -EFAULT;
return 0;
}
case VIDIOCGCHAN:
{
struct video_channel v;
if(copy_from_user(&v, arg,sizeof(v)))
return -EFAULT;
v.flags=VIDEO_VC_AUDIO;
v.tuners=0;
v.type=VIDEO_TYPE_CAMERA;
v.norm = btv->win.norm;
if (v.channel>=bttv_tvcards[btv->type].video_inputs)
return -EINVAL;
if(v.channel==bttv_tvcards[btv->type].tuner)
{
strcpy(v.name,"Television");
v.flags|=VIDEO_VC_TUNER;
v.type=VIDEO_TYPE_TV;
v.tuners=1;
}
else if(v.channel==bttv_tvcards[btv->type].svhs)
strcpy(v.name,"S-Video");
else
sprintf(v.name,"Composite%d",v.channel);
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
return 0;
}
/*
* Each channel has 1 tuner
*/
case VIDIOCSCHAN:
{
struct video_channel v;
if(copy_from_user(&v, arg,sizeof(v)))
return -EFAULT;
if (v.channel>bttv_tvcards[btv->type].video_inputs)
return -EINVAL;
if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms)))
return -EOPNOTSUPP;
bttv_call_i2c_clients(btv,cmd,&v);
down(&btv->lock);
bt848_muxsel(btv, v.channel);
btv->channel=v.channel;
if (btv->win.norm != v.norm) {
btv->win.norm = v.norm;
make_vbitab(btv);
spin_lock_irqsave(&btv->s_lock, irq_flags);
bt848_set_winsize(btv);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
}
up(&btv->lock);
return 0;
}
case VIDIOCGTUNER:
{
struct video_tuner v;
if(copy_from_user(&v,arg,sizeof(v))!=0)
return -EFAULT;
#if 0 /* tuner.signal might be of intrest for non-tuner sources too ... */
if(v.tuner||btv->channel) /* Only tuner 0 */
return -EINVAL;
#endif
strcpy(v.name, "Television");
v.rangelow=0;
v.rangehigh=0xFFFFFFFF;
v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM;
v.mode = btv->win.norm;
v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0;
bttv_call_i2c_clients(btv,cmd,&v);
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
return 0;
}
/* We have but one tuner */
case VIDIOCSTUNER:
{
struct video_tuner v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
/* Only one channel has a tuner */
if(v.tuner!=bttv_tvcards[btv->type].tuner)
return -EINVAL;
if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC
&&v.mode!=VIDEO_MODE_SECAM)
return -EOPNOTSUPP;
bttv_call_i2c_clients(btv,cmd,&v);
if (btv->win.norm != v.mode) {
btv->win.norm = v.mode;
down(&btv->lock);
set_pll(btv);
make_vbitab(btv);
spin_lock_irqsave(&btv->s_lock, irq_flags);
bt848_set_winsize(btv);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
up(&btv->lock);
}
return 0;
}
case VIDIOCGPICT:
{
struct video_picture p=btv->picture;
if(copy_to_user(arg, &p, sizeof(p)))
return -EFAULT;
return 0;
}
case VIDIOCSPICT:
{
struct video_picture p;
if(copy_from_user(&p, arg,sizeof(p)))
return -EFAULT;
if (p.palette > PALETTEFMT_MAX)
return -EINVAL;
down(&btv->lock);
/* We want -128 to 127 we get 0-65535 */
bt848_bright(btv, (p.brightness>>8)-128);
/* 0-511 for the colour */
bt848_sat_u(btv, p.colour>>7);
bt848_sat_v(btv, ((p.colour>>7)*201L)/237);
/* -128 to 127 */
bt848_hue(btv, (p.hue>>8)-128);
/* 0-511 */
bt848_contrast(btv, p.contrast>>7);
btv->picture = p;
up(&btv->lock);
return 0;
}
case VIDIOCSWIN:
{
struct video_window vw;
struct video_clip *vcp = NULL;
if(copy_from_user(&vw,arg,sizeof(vw)))
return -EFAULT;
down(&btv->lock);
if(vw.flags || vw.width < 16 || vw.height < 16)
{
spin_lock_irqsave(&btv->s_lock, irq_flags);
btv->scr_on = 0;
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
up(&btv->lock);
return -EINVAL;
}
if (btv->win.bpp < 4)
{ /* adjust and align writes */
vw.x = (vw.x + 3) & ~3;
vw.width &= ~3;
}
if (btv->needs_restart)
bt848_restart(btv);
btv->win.x=vw.x;
btv->win.y=vw.y;
btv->win.width=vw.width;
btv->win.height=vw.height;
spin_lock_irqsave(&btv->s_lock, irq_flags);
bt848_set_risc_jmps(btv,0);
bt848_set_winsize(btv);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
/*
* Do any clips.
*/
if(vw.clipcount<0) {
if((vcp=vmalloc(VIDEO_CLIPMAP_SIZE))==NULL) {
up(&btv->lock);
return -ENOMEM;
}
if(copy_from_user(vcp, vw.clips,
VIDEO_CLIPMAP_SIZE)) {
up(&btv->lock);
vfree(vcp);
return -EFAULT;
}
} else if (vw.clipcount > 2048) {
up(&btv->lock);
return -EINVAL;
} else if (vw.clipcount) {
if((vcp=vmalloc(sizeof(struct video_clip)*
(vw.clipcount))) == NULL) {
up(&btv->lock);
return -ENOMEM;
}
if(copy_from_user(vcp,vw.clips,
sizeof(struct video_clip)*
vw.clipcount)) {
up(&btv->lock);
vfree(vcp);
return -EFAULT;
}
}
make_clip_tab(btv, vcp, vw.clipcount);
if (vw.clipcount != 0)
vfree(vcp);
spin_lock_irqsave(&btv->s_lock, irq_flags);
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
up(&btv->lock);
return 0;
}
case VIDIOCGWIN:
{
struct video_window vw;
memset(&vw,0,sizeof(vw));
vw.x=btv->win.x;
vw.y=btv->win.y;
vw.width=btv->win.width;
vw.height=btv->win.height;
if(btv->win.interlace)
vw.flags|=VIDEO_WINDOW_INTERLACE;
if(copy_to_user(arg,&vw,sizeof(vw)))
return -EFAULT;
return 0;
}
case VIDIOCCAPTURE:
{
int v;
if(copy_from_user(&v, arg,sizeof(v)))
return -EFAULT;
if(btv->win.vidadr == 0)
return -EINVAL;
if (btv->win.width==0 || btv->win.height==0)
return -EINVAL;
if (1 == no_overlay)
return -EIO;
spin_lock_irqsave(&btv->s_lock, irq_flags);
if (v == 1 && btv->win.vidadr != 0)
btv->scr_on = 1;
if (v == 0)
btv->scr_on = 0;
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
return 0;
}
case VIDIOCGFBUF:
{
struct video_buffer v;
v.base=(void *)btv->win.vidadr;
v.height=btv->win.sheight;
v.width=btv->win.swidth;
v.depth=btv->win.depth;
v.bytesperline=btv->win.bpl;
if(copy_to_user(arg, &v,sizeof(v)))
return -EFAULT;
return 0;
}
case VIDIOCSFBUF:
{
struct video_buffer v;
if(!capable(CAP_SYS_ADMIN) &&
!capable(CAP_SYS_RAWIO))
return -EPERM;
if(copy_from_user(&v, arg,sizeof(v)))
return -EFAULT;
if(v.depth!=8 && v.depth!=15 && v.depth!=16 &&
v.depth!=24 && v.depth!=32 && v.width > 16 &&
v.height > 16 && v.bytesperline > 16)
return -EINVAL;
down(&btv->lock);
if (v.base)
btv->win.vidadr=(unsigned long)v.base;
btv->win.sheight=v.height;
btv->win.swidth=v.width;
btv->win.bpp=((v.depth+7)&0x38)/8;
btv->win.depth=v.depth;
btv->win.bpl=v.bytesperline;
#if 0 /* was broken for ages and nobody noticed. Looks like we don't need
it any more as everybody explicitly sets the palette using VIDIOCSPICT
these days */
/* set sefault color format */
switch (v.depth) {
case 8: btv->picture.palette = VIDEO_PALETTE_HI240; break;
case 15: btv->picture.palette = VIDEO_PALETTE_RGB555; break;
case 16: btv->picture.palette = VIDEO_PALETTE_RGB565; break;
case 24: btv->picture.palette = VIDEO_PALETTE_RGB24; break;
case 32: btv->picture.palette = VIDEO_PALETTE_RGB32; break;
}
#endif
if (bttv_debug)
printk("Display at %p is %d by %d, bytedepth %d, bpl %d\n",
v.base, v.width,v.height, btv->win.bpp, btv->win.bpl);
spin_lock_irqsave(&btv->s_lock, irq_flags);
bt848_set_winsize(btv);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
up(&btv->lock);
return 0;
}
case VIDIOCKEY:
{
/* Will be handled higher up .. */
return 0;
}
case VIDIOCGFREQ:
{
unsigned long v=btv->win.freq;
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
return 0;
}
case VIDIOCSFREQ:
{
unsigned long v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
btv->win.freq=v;
bttv_call_i2c_clients(btv,cmd,&v);
#if 1
if (btv->radio && btv->has_matchbox)
tea5757_set_freq(btv,v);
#endif
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");
v.mode = VIDEO_SOUND_MONO;
bttv_call_i2c_clients(btv,cmd,&v);
/* card specific hooks */
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&v,0);
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;
down(&btv->lock);
if(v.flags&VIDEO_AUDIO_MUTE)
audio(btv, AUDIO_MUTE, 1);
/* One audio source per tuner -- huh? <GA> */
if(v.audio<0 || v.audio >= bttv_tvcards[btv->type].audio_inputs) {
up(&btv->lock);
return -EINVAL;
}
/* bt848_muxsel(btv,v.audio); */
if(!(v.flags&VIDEO_AUDIO_MUTE))
audio(btv, AUDIO_UNMUTE, 1);
bttv_call_i2c_clients(btv,cmd,&v);
/* card specific hooks */
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&v,1);
btv->audio_dev=v;
up(&btv->lock);
return 0;
}
case VIDIOCSYNC:
{
DECLARE_WAITQUEUE(wait, current);
if(copy_from_user((void *)&i,arg,sizeof(int)))
return -EFAULT;
if (i < 0 || i >= gbuffers)
return -EINVAL;
switch (btv->gbuf[i].stat) {
case GBUFFER_UNUSED:
ret = -EINVAL;
break;
case GBUFFER_GRABBING:
add_wait_queue(&btv->capq, &wait);
current->state = TASK_INTERRUPTIBLE;
while(btv->gbuf[i].stat==GBUFFER_GRABBING) {
if (bttv_debug)
printk("bttv%d: cap sync: sleep on %d\n",btv->nr,i);
schedule();
if(signal_pending(current)) {
remove_wait_queue(&btv->capq, &wait);
current->state = TASK_RUNNING;
return -EINTR;
}
}
remove_wait_queue(&btv->capq, &wait);
current->state = TASK_RUNNING;
/* fall throuth */
case GBUFFER_DONE:
case GBUFFER_ERROR:
ret = (btv->gbuf[i].stat == GBUFFER_ERROR) ? -EIO : 0;
if (bttv_debug)
printk("bttv%d: cap sync: buffer %d, retval %d\n",btv->nr,i,ret);
btv->gbuf[i].stat = GBUFFER_UNUSED;
}
if (btv->needs_restart) {
down(&btv->lock);
bt848_restart(btv);
up(&btv->lock);
}
return ret;
}
case BTTV_FIELDNR:
if(copy_to_user((void *) arg, (void *) &btv->last_field,
sizeof(btv->last_field)))
return -EFAULT;
break;
case BTTV_PLLSET: {
struct bttv_pll_info p;
if(!capable(CAP_SYS_ADMIN))
return -EPERM;
if(copy_from_user(&p , (void *) arg, sizeof(btv->pll)))
return -EFAULT;
down(&btv->lock);
btv->pll.pll_ifreq = p.pll_ifreq;
btv->pll.pll_ofreq = p.pll_ofreq;
btv->pll.pll_crystal = p.pll_crystal;
up(&btv->lock);
break;
}
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
int ret;
if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm)))
return -EFAULT;
down(&btv->lock);
ret = vgrab(btv, &vm);
up(&btv->lock);
return ret;
}
case VIDIOCGMBUF:
{
struct video_mbuf vm;
memset(&vm, 0 , sizeof(vm));
vm.size=gbufsize*gbuffers;
vm.frames=gbuffers;
for (i = 0; i < gbuffers; i++)
vm.offsets[i]=i*gbufsize;
if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
return -EFAULT;
return 0;
}
case VIDIOCGUNIT:
{
struct video_unit vu;
vu.video=btv->video_dev.minor;
vu.vbi=btv->vbi_dev.minor;
if(btv->radio_dev.minor!=-1)
vu.radio=btv->radio_dev.minor;
else
vu.radio=VIDEO_NO_UNIT;
vu.audio=VIDEO_NO_UNIT;
vu.teletext=VIDEO_NO_UNIT;
if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
return -EFAULT;
return 0;
}
case BTTV_BURST_ON:
{
burst(1);
return 0;
}
case BTTV_BURST_OFF:
{
burst(0);
return 0;
}
case BTTV_VERSION:
{
return BTTV_VERSION_CODE;
}
case BTTV_PICNR:
{
/* return picture;*/
return 0;
}
default:
return -ENOIOCTLCMD;
}
return 0;
}
/*
* This maps the vmalloced and reserved fbuffer to user space.
*
* FIXME:
* - PAGE_READONLY should suffice!?
* - remap_page_range is kind of inefficient for page by page remapping.
* But e.g. pte_alloc() does not work in modules ... :-(
*/
static int do_bttv_mmap(struct vm_area_struct *vma, struct bttv *btv, const char *adr, unsigned long size)
{
unsigned long start=(unsigned long) adr;
unsigned long page,pos;
if (size>gbuffers*gbufsize)
return -EINVAL;
if (!btv->fbuffer) {
if(fbuffer_alloc(btv))
return -EINVAL;
}
pos=(unsigned long) btv->fbuffer;
while (size > 0) {
page = kvirt_to_pa(pos);
if (remap_page_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start+=PAGE_SIZE;
pos+=PAGE_SIZE;
size-=PAGE_SIZE;
}
return 0;
}
static int bttv_mmap(struct vm_area_struct *vma, struct video_device *dev, const char *adr, unsigned long size)
{
struct bttv *btv=(struct bttv *)dev;
int r;
down(&btv->lock);
r=do_bttv_mmap(vma, btv, adr, size);
up(&btv->lock);
return r;
}
static struct video_device bttv_template=
{
owner: THIS_MODULE,
name: "UNSET",
type: VID_TYPE_TUNER|VID_TYPE_CAPTURE|VID_TYPE_OVERLAY|VID_TYPE_TELETEXT,
hardware: VID_HARDWARE_BT848,
open: bttv_open,
close: bttv_close,
read: bttv_read,
write: bttv_write,
ioctl: bttv_ioctl,
mmap: bttv_mmap,
minor: -1,
};
static long vbi_read(struct video_device *v, char *buf, unsigned long count,
int nonblock)
{
struct bttv *btv=(struct bttv *)(v-2);
int q,todo;
DECLARE_WAITQUEUE(wait, current);
todo=count;
while (todo && todo>(q=VBIBUF_SIZE-btv->vbip))
{
if (btv->needs_restart) {
down(&btv->lock);
bt848_restart(btv);
up(&btv->lock);
}
if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, q))
return -EFAULT;
todo-=q;
buf+=q;
add_wait_queue(&btv->vbiq, &wait);
current->state = TASK_INTERRUPTIBLE;
if (todo && q==VBIBUF_SIZE-btv->vbip)
{
if(nonblock)
{
remove_wait_queue(&btv->vbiq, &wait);
current->state = TASK_RUNNING;
if(count==todo)
return -EWOULDBLOCK;
return count-todo;
}
schedule();
if(signal_pending(current))
{
remove_wait_queue(&btv->vbiq, &wait);
current->state = TASK_RUNNING;
if(todo==count)
return -EINTR;
else
return count-todo;
}
}
remove_wait_queue(&btv->vbiq, &wait);
current->state = TASK_RUNNING;
}
if (todo)
{
if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo))
return -EFAULT;
btv->vbip+=todo;
}
return count;
}
static unsigned int vbi_poll(struct video_device *dev, struct file *file,
poll_table *wait)
{
struct bttv *btv=(struct bttv *)(dev-2);
unsigned int mask = 0;
poll_wait(file, &btv->vbiq, wait);
if (btv->vbip < VBIBUF_SIZE)
mask |= (POLLIN | POLLRDNORM);
return mask;
}
static int vbi_open(struct video_device *dev, int flags)
{
struct bttv *btv=(struct bttv *)(dev-2);
unsigned long irq_flags;
down(&btv->lock);
if (btv->needs_restart)
bt848_restart(btv);
set_pll(btv);
btv->vbip=VBIBUF_SIZE;
spin_lock_irqsave(&btv->s_lock, irq_flags);
btv->vbi_on = 1;
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
up(&btv->lock);
return 0;
}
static void vbi_close(struct video_device *dev)
{
struct bttv *btv=(struct bttv *)(dev-2);
unsigned long irq_flags;
spin_lock_irqsave(&btv->s_lock, irq_flags);
btv->vbi_on = 0;
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
}
static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
{
struct bttv *btv=(struct bttv *)(dev-2);
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability b;
strcpy(b.name,btv->vbi_dev.name);
b.type = ((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) |
VID_TYPE_TELETEXT;
b.channels = 0;
b.audios = 0;
b.maxwidth = 0;
b.maxheight = 0;
b.minwidth = 0;
b.minheight = 0;
if(copy_to_user(arg,&b,sizeof(b)))
return -EFAULT;
return 0;
}
case VIDIOCGFREQ:
case VIDIOCSFREQ:
case VIDIOCGTUNER:
case VIDIOCSTUNER:
case VIDIOCGCHAN:
case VIDIOCSCHAN:
case BTTV_VERSION:
return bttv_ioctl(dev-2,cmd,arg);
case BTTV_VBISIZE:
/* make alevt happy :-) */
return VBIBUF_SIZE;
default:
return -EINVAL;
}
}
static struct video_device vbi_template=
{
owner: THIS_MODULE,
name: "bttv vbi",
type: VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
hardware: VID_HARDWARE_BT848,
open: vbi_open,
close: vbi_close,
read: vbi_read,
write: bttv_write,
poll: vbi_poll,
ioctl: vbi_ioctl,
minor: -1,
};
static int radio_open(struct video_device *dev, int flags)
{
struct bttv *btv = (struct bttv *)(dev-1);
unsigned long v;
down(&btv->lock);
if (btv->user)
goto busy_unlock;
btv->user++;
btv->radio = 1;
v = 400*16;
bttv_call_i2c_clients(btv,VIDIOCSFREQ,&v);
bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type);
bt848_muxsel(btv,0);
up(&btv->lock);
return 0;
busy_unlock:
up(&btv->lock);
return -EBUSY;
}
static void radio_close(struct video_device *dev)
{
struct bttv *btv=(struct bttv *)(dev-1);
down(&btv->lock);
btv->user--;
btv->radio = 0;
up(&btv->lock);
}
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);
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability v;
strcpy(v.name,btv->video_dev.name);
v.type = VID_TYPE_TUNER;
v.channels = 1;
v.audios = 1;
/* No we don't do pictures */
v.maxwidth = 0;
v.maxheight = 0;
v.minwidth = 0;
v.minheight = 0;
if (copy_to_user(arg, &v, sizeof(v)))
return -EFAULT;
return 0;
break;
}
case VIDIOCGTUNER:
{
struct video_tuner v;
if(copy_from_user(&v,arg,sizeof(v))!=0)
return -EFAULT;
if(v.tuner||btv->channel) /* Only tuner 0 */
return -EINVAL;
strcpy(v.name, "Radio");
/* japan: 76.0 MHz - 89.9 MHz
western europe: 87.5 MHz - 108.0 MHz
russia: 65.0 MHz - 108.0 MHz */
v.rangelow=(int)(65*16);
v.rangehigh=(int)(108*16);
v.flags= 0; /* XXX */
v.mode = 0; /* XXX */
bttv_call_i2c_clients(btv,cmd,&v);
if(copy_to_user(arg,&v,sizeof(v)))
return -EFAULT;
return 0;
}
case VIDIOCSTUNER:
{
struct video_tuner v;
if(copy_from_user(&v, arg, sizeof(v)))
return -EFAULT;
/* Only channel 0 has a tuner */
if(v.tuner!=0 || btv->channel)
return -EINVAL;
/* XXX anything to do ??? */
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=
{
owner: THIS_MODULE,
name: "bttv radio",
type: VID_TYPE_TUNER,
hardware: VID_HARDWARE_BT848,
open: radio_open,
close: radio_close,
read: radio_read, /* just returns -EINVAL */
write: bttv_write, /* just returns -EINVAL */
ioctl: radio_ioctl,
minor: -1,
};
static void bt848_set_risc_jmps(struct bttv *btv, int flags)
{
if (-1 == flags) {
/* defaults */
flags = 0;
if (btv->scr_on)
flags |= 0x03;
if (btv->vbi_on)
flags |= 0x0c;
}
if (bttv_debug > 1)
printk("bttv%d: set_risc_jmp %08lx:",
btv->nr,virt_to_bus(btv->risc_jmp));
/* Sync to start of odd field */
btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC
|BT848_FIFO_STATUS_VRE);
btv->risc_jmp[1]=cpu_to_le32(0);
/* Jump to odd vbi sub */
btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20));
if (flags&8) {
if (bttv_debug > 1)
printk(" ev=%08lx",virt_to_bus(btv->vbi_odd));
btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->vbi_odd));
} else {
if (bttv_debug > 1)
printk(" -----------");
btv->risc_jmp[3]=cpu_to_le32(virt_to_bus(btv->risc_jmp+4));
}
/* Jump to odd sub */
btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20));
if (0 != btv->risc_cap_odd) {
if (bttv_debug > 1)
printk(" e%d=%08x",btv->gq_grab,btv->risc_cap_odd);
flags |= 3;
btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd);
} else if ((flags&2) &&
(!btv->win.interlace || 0 == btv->risc_cap_even)) {
if (bttv_debug > 1)
printk(" eo=%08lx",virt_to_bus(btv->risc_scr_odd));
btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_scr_odd));
} else {
if (bttv_debug > 1)
printk(" -----------");
btv->risc_jmp[5]=cpu_to_le32(virt_to_bus(btv->risc_jmp+6));
}
/* Sync to start of even field */
btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC
|BT848_FIFO_STATUS_VRO);
btv->risc_jmp[7]=cpu_to_le32(0);
/* Jump to even vbi sub */
btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP);
if (flags&4) {
if (bttv_debug > 1)
printk(" ov=%08lx",virt_to_bus(btv->vbi_even));
btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->vbi_even));
} else {
if (bttv_debug > 1)
printk(" -----------");
btv->risc_jmp[9]=cpu_to_le32(virt_to_bus(btv->risc_jmp+10));
}
/* Jump to even sub */
btv->risc_jmp[10]=cpu_to_le32(BT848_RISC_JUMP|(8<<20));
if (0 != btv->risc_cap_even) {
if (bttv_debug > 1)
printk(" o%d=%08x",btv->gq_grab,btv->risc_cap_even);
flags |= 3;
btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even);
} else if ((flags&1) &&
btv->win.interlace) {
if (bttv_debug > 1)
printk(" oo=%08lx",virt_to_bus(btv->risc_scr_even));
btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_scr_even));
} else {
if (bttv_debug > 1)
printk(" -----------");
btv->risc_jmp[11]=cpu_to_le32(virt_to_bus(btv->risc_jmp+12));
}
if (btv->gq_start) {
btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ);
} else {
btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP);
}
btv->risc_jmp[13]=cpu_to_le32(virt_to_bus(btv->risc_jmp));
/* enable cpaturing and DMA */
if (bttv_debug > 1)
printk(" flags=0x%x dma=%s\n",
flags,(flags&0x0f) ? "on" : "off");
btaor(flags, ~0x0f, BT848_CAP_CTL);
if (flags&0x0f)
bt848_dma(btv, 3);
else
bt848_dma(btv, 0);
}
# define do_video_register(dev,type,nr) video_register_device(dev,type,nr)
static int __devinit init_video_dev(struct bttv *btv)
{
audio(btv, AUDIO_MUTE, 1);
if(do_video_register(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0)
return -1;
if(do_video_register(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0)
{
video_unregister_device(&btv->video_dev);
return -1;
}
if (btv->has_radio)
{
if(do_video_register(&btv->radio_dev, VFL_TYPE_RADIO, radio_nr)<0)
{
video_unregister_device(&btv->vbi_dev);
video_unregister_device(&btv->video_dev);
return -1;
}
}
return 1;
}
static int __devinit init_bt848(struct bttv *btv)
{
int j;
unsigned long irq_flags;
btv->user=0;
init_MUTEX(&btv->lock);
/* dump current state of the gpio registers before changing them,
* might help to make a new card work */
if (bttv_gpio)
bttv_gpio_tracking(btv,"init #1");
/* reset the bt848 */
btwrite(0, BT848_SRESET);
DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n", btv->nr, (unsigned long) btv->bt848_mem));
/* not registered yet */
btv->video_dev.minor = -1;
btv->radio_dev.minor = -1;
btv->vbi_dev.minor = -1;
/* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */
btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */
btv->win.interlace=1;
btv->win.x=0;
btv->win.y=0;
btv->win.width=320;
btv->win.height=240;
btv->win.bpp=2;
btv->win.depth=16;
btv->win.color_fmt=BT848_COLOR_FMT_RGB16;
btv->win.bpl=1024*btv->win.bpp;
btv->win.swidth=1024;
btv->win.sheight=768;
btv->win.vidadr=0;
btv->vbi_on=0;
btv->scr_on=0;
btv->risc_scr_odd=0;
btv->risc_scr_even=0;
btv->risc_cap_odd=0;
btv->risc_cap_even=0;
btv->risc_jmp=0;
btv->vbibuf=0;
btv->field=btv->last_field=0;
btv->errors=0;
btv->needs_restart=0;
btv->has_radio=radio[btv->nr];
if (!(btv->risc_scr_odd=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
return -1;
if (!(btv->risc_scr_even=(unsigned int *) kmalloc(RISCMEM_LEN/2, GFP_KERNEL)))
return -1;
if (!(btv->risc_jmp =(unsigned int *) kmalloc(2048, GFP_KERNEL)))
return -1;
btv->vbi_odd=btv->risc_jmp+16;
btv->vbi_even=btv->vbi_odd+256;
btv->bus_vbi_odd=virt_to_bus(btv->risc_jmp+12);
btv->bus_vbi_even=virt_to_bus(btv->risc_jmp+6);
btwrite(virt_to_bus(btv->risc_jmp+2), BT848_RISC_STRT_ADD);
btv->vbibuf=(unsigned char *) vmalloc_32(VBIBUF_SIZE);
if (!btv->vbibuf)
return -1;
if (!(btv->gbuf = kmalloc(sizeof(struct bttv_gbuf)*gbuffers,GFP_KERNEL)))
return -1;
for (j = 0; j < gbuffers; j++) {
if (!(btv->gbuf[j].risc = kmalloc(16384,GFP_KERNEL)))
return -1;
}
memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random
memory to the user */
btv->fbuffer=NULL;
/* btwrite(0, BT848_TDEC); */
btwrite(0x10, BT848_COLOR_CTL);
btwrite(0x00, BT848_CAP_CTL);
/* set planar and packed mode trigger points and */
/* set rising edge of inverted GPINTR pin as irq trigger */
btwrite(BT848_GPIO_DMA_CTL_PKTP_32|
BT848_GPIO_DMA_CTL_PLTP1_16|
BT848_GPIO_DMA_CTL_PLTP23_16|
BT848_GPIO_DMA_CTL_GPINTC|
BT848_GPIO_DMA_CTL_GPINTI,
BT848_GPIO_DMA_CTL);
/* select direct input */
btwrite(0x00, BT848_GPIO_REG_INP);
btwrite(0x00, BT848_GPIO_OUT_EN);
if (bttv_gpio)
bttv_gpio_tracking(btv,"init #2");
btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO,
BT848_IFORM);
btwrite(0xd8, BT848_CONTRAST_LO);
bt848_bright(btv, 0x10);
btwrite(0x20, BT848_E_VSCALE_HI);
btwrite(0x20, BT848_O_VSCALE_HI);
btwrite(/*BT848_ADC_SYNC_T|*/
BT848_ADC_RESERVED|BT848_ADC_CRUSH, BT848_ADC);
if (lumafilter) {
btwrite(0, BT848_E_CONTROL);
btwrite(0, BT848_O_CONTROL);
} else {
btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL);
btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL);
}
btv->picture.colour=254<<7;
btv->picture.brightness=128<<8;
btv->picture.hue=128<<8;
btv->picture.contrast=0xd8<<7;
btwrite(0x00, BT848_E_SCLOOP);
btwrite(0x00, BT848_O_SCLOOP);
/* clear interrupt status */
btwrite(0xfffffUL, BT848_INT_STAT);
/* set interrupt mask */
btwrite(btv->triton1|
/*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR|
BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/
(fieldnr ? BT848_INT_VSYNC : 0)|
BT848_INT_GPINT|
BT848_INT_SCERR|
BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES|
BT848_INT_FMTCHG|BT848_INT_HLOCK,
BT848_INT_MASK);
bt848_muxsel(btv, 1);
bt848_set_winsize(btv);
make_vbitab(btv);
spin_lock_irqsave(&btv->s_lock, irq_flags);
bt848_set_risc_jmps(btv,-1);
spin_unlock_irqrestore(&btv->s_lock, irq_flags);
/* needs to be done before i2c is registered */
if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
bttv_boot_msp34xx(btv,5);
if (btv->type == BTTV_VOODOOTV_FM)
bttv_boot_msp34xx(btv,20);
/* register i2c */
btv->tuner_type=-1;
init_bttv_i2c(btv);
/* some card-specific stuff (needs working i2c) */
bttv_init_card(btv);
/*
* Now add the template and register the device unit.
*/
init_video_dev(btv);
return 0;
}
/* ----------------------------------------------------------------------- */
static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK",
"VPRES", "6", "7", "I2CDONE", "GPINT", "10",
"RISCI", "FBUS", "FTRGT", "FDSR", "PPERR",
"RIPERR", "PABORT", "OCERR", "SCERR" };
static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
{
u32 stat,astat;
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(stat,BT848_INT_STAT);
/* get device status bits */
dstat=btread(BT848_DSTATUS);
if (irq_debug) {
int i;
printk(KERN_DEBUG "bttv%d: irq loop=%d risc=%x, bits:",
btv->nr, count, stat>>28);
for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) {
if (stat & (1 << i))
printk(" %s",irq_name[i]);
if (astat & (1 << i))
printk("*");
}
if (stat & BT848_INT_HLOCK)
printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC)
? "yes" : "no");
if (stat & BT848_INT_VPRES)
printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES)
? "yes" : "no");
if (stat & BT848_INT_FMTCHG)
printk(" NUML => %s", (dstat & BT848_DSTATUS_PRES)
? "625" : "525");
printk("\n");
}
if (astat&BT848_INT_GPINT)
wake_up_interruptible(&btv->gpioq);
if (astat&BT848_INT_VSYNC)
btv->field++;
if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) {
if (bttv_verbose)
printk("bttv%d: irq:%s%s risc_count=%08x\n",
btv->nr,
(astat&BT848_INT_SCERR) ? " SCERR" : "",
(astat&BT848_INT_OCERR) ? " OCERR" : "",
btread(BT848_RISC_COUNT));
btv->errors++;
if (btv->errors < BTTV_ERRORS) {
spin_lock(&btv->s_lock);
btand(~15, BT848_GPIO_DMA_CTL);
btwrite(virt_to_bus(btv->risc_jmp+2),
BT848_RISC_STRT_ADD);
bt848_set_geo(btv,0);
bt848_set_risc_jmps(btv,-1);
spin_unlock(&btv->s_lock);
} else {
if (bttv_verbose)
printk("bttv%d: aiee: error loops\n",btv->nr);
bt848_offline(btv);
}
}
if (astat&BT848_INT_RISCI)
{
if (bttv_debug > 1)
printk("bttv%d: IRQ_RISCI\n",btv->nr);
/* captured VBI frame */
if (stat&(1<<28))
{
btv->vbip=0;
/* inc vbi frame count for detecting drops */
(*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++;
wake_up_interruptible(&btv->vbiq);
}
/* captured full frame */
if (stat&(2<<28) && btv->gq_grab != -1)
{
btv->last_field=btv->field;
if (bttv_debug)
printk("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab);
do_gettimeofday(&btv->gbuf[btv->gq_grab].tv);
spin_lock(&btv->s_lock);
btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE;
btv->gq_grab = -1;
if (btv->gq_in != btv->gq_out)
{
btv->gq_grab = btv->gqueue[btv->gq_out++];
btv->gq_out = btv->gq_out % MAX_GBUFFERS;
if (bttv_debug)
printk("bttv%d: cap irq: capture %d\n",btv->nr,btv->gq_grab);
btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro;
btv->risc_cap_even = btv->gbuf[btv->gq_grab].re;
bt848_set_risc_jmps(btv,-1);
bt848_set_geo(btv,0);
btwrite(BT848_COLOR_CTL_GAMMA,
BT848_COLOR_CTL);
} else {
btv->risc_cap_odd = 0;
btv->risc_cap_even = 0;
bt848_set_risc_jmps(btv,-1);
bt848_set_geo(btv,0);
btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA,
BT848_COLOR_CTL);
}
spin_unlock(&btv->s_lock);
wake_up_interruptible(&btv->capq);
break;
}
if (stat&(8<<28) && btv->gq_start)
{
spin_lock(&btv->s_lock);
btv->gq_start = 0;
btv->gq_grab = btv->gqueue[btv->gq_out++];
btv->gq_out = btv->gq_out % MAX_GBUFFERS;
if (bttv_debug)
printk("bttv%d: cap irq: capture %d [start]\n",btv->nr,btv->gq_grab);
btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro;
btv->risc_cap_even = btv->gbuf[btv->gq_grab].re;
bt848_set_risc_jmps(btv,-1);
bt848_set_geo(btv,0);
btwrite(BT848_COLOR_CTL_GAMMA,
BT848_COLOR_CTL);
spin_unlock(&btv->s_lock);
}
}
if (astat&BT848_INT_HLOCK) {
if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio))
audio(btv, AUDIO_ON,0);
else
audio(btv, AUDIO_OFF,0);
}
count++;
if (count > 20) {
btwrite(0, BT848_INT_MASK);
printk(KERN_ERR
"bttv%d: IRQ lockup, cleared int mask\n", btv->nr);
bt848_offline(btv);
}
}
}
/*
* Scan for a Bt848 card, request the irq and map the io memory
*/
static void __devexit bttv_remove(struct pci_dev *pci_dev)
{
u8 command;
int j;
struct bttv *btv = pci_get_drvdata(pci_dev);
if (bttv_verbose)
printk("bttv%d: unloading\n",btv->nr);
/* unregister i2c_bus */
if (0 == btv->i2c_rc)
i2c_bit_del_bus(&btv->i2c_adap);
/* turn off all capturing, DMA and IRQs */
btand(~15, BT848_GPIO_DMA_CTL);
/* first disable interrupts before unmapping the memory! */
btwrite(0, BT848_INT_MASK);
btwrite(~0x0UL,BT848_INT_STAT);
btwrite(0x0, BT848_GPIO_OUT_EN);
if (bttv_gpio)
bttv_gpio_tracking(btv,"cleanup");
/* disable PCI bus-mastering */
pci_read_config_byte(btv->dev, PCI_COMMAND, &command);
command &= ~PCI_COMMAND_MASTER;
pci_write_config_byte(btv->dev, PCI_COMMAND, command);
/* unmap and free memory */
for (j = 0; j < gbuffers; j++)
if (btv->gbuf[j].risc)
kfree(btv->gbuf[j].risc);
if (btv->gbuf)
kfree((void *) btv->gbuf);
if (btv->risc_scr_odd)
kfree((void *) btv->risc_scr_odd);
if (btv->risc_scr_even)
kfree((void *) btv->risc_scr_even);
DEBUG(printk(KERN_DEBUG "free: risc_jmp: 0x%p.\n", btv->risc_jmp));
if (btv->risc_jmp)
kfree((void *) btv->risc_jmp);
DEBUG(printk(KERN_DEBUG "bt848_vbibuf: 0x%p.\n", btv->vbibuf));
if (btv->vbibuf)
vfree((void *) btv->vbibuf);
free_irq(btv->irq,btv);
DEBUG(printk(KERN_DEBUG "bt848_mem: 0x%p.\n", btv->bt848_mem));
if (btv->bt848_mem)
iounmap(btv->bt848_mem);
if (btv->video_dev.minor!=-1)
video_unregister_device(&btv->video_dev);
if (btv->vbi_dev.minor!=-1)
video_unregister_device(&btv->vbi_dev);
if (btv->radio_dev.minor != -1)
video_unregister_device(&btv->radio_dev);
release_mem_region(pci_resource_start(btv->dev,0),
pci_resource_len(btv->dev,0));
/* wake up any waiting processes
because shutdown flag is set, no new processes (in this queue)
are expected
*/
btv->shutdown=1;
wake_up(&btv->gpioq);
pci_set_drvdata(pci_dev, NULL);
return;
}
static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id)
{
int result;
unsigned char lat;
struct bttv *btv;
#if defined(__powerpc__)
unsigned int cmd;
#endif
printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
btv=&bttvs[bttv_num];
btv->dev=dev;
btv->nr = bttv_num;
btv->bt848_mem=NULL;
btv->vbibuf=NULL;
btv->risc_jmp=NULL;
btv->vbi_odd=NULL;
btv->vbi_even=NULL;
init_waitqueue_head(&btv->vbiq);
init_waitqueue_head(&btv->capq);
btv->vbip=VBIBUF_SIZE;
btv->s_lock = SPIN_LOCK_UNLOCKED;
init_waitqueue_head(&btv->gpioq);
btv->shutdown=0;
memcpy(&btv->video_dev,&bttv_template, sizeof(bttv_template));
memcpy(&btv->vbi_dev,&vbi_template, sizeof(vbi_template));
memcpy(&btv->radio_dev,&radio_template,sizeof(radio_template));
btv->id=dev->device;
btv->irq=dev->irq;
btv->bt848_adr=pci_resource_start(dev,0);
if (pci_enable_device(dev))
return -EIO;
if (!request_mem_region(pci_resource_start(dev,0),
pci_resource_len(dev,0),
"bttv")) {
return -EBUSY;
}
if (btv->id >= 878)
btv->i2c_command = 0x83;
else
btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA);
pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %02x:%02x.%x, ",
bttv_num,btv->id, btv->revision, dev->bus->number,
PCI_SLOT(dev->devfn),PCI_FUNC(dev->devfn));
printk("irq: %d, latency: %d, memory: 0x%lx\n",
btv->irq, lat, btv->bt848_adr);
bttv_idcard(btv);
#if defined(__powerpc__)
/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */
/* response on cards with no firmware is not enabled by OF */
pci_read_config_dword(dev, PCI_COMMAND, &cmd);
cmd = (cmd | PCI_COMMAND_MEMORY );
pci_write_config_dword(dev, PCI_COMMAND, cmd);
#endif
#ifdef __sparc__
btv->bt848_mem=(unsigned char *)btv->bt848_adr;
#else
btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000);
#endif
/* clear interrupt mask */
btwrite(0, BT848_INT_MASK);
result = request_irq(btv->irq, bttv_irq,
SA_SHIRQ | SA_INTERRUPT,"bttv",(void *)btv);
if (result==-EINVAL)
{
printk(KERN_ERR "bttv%d: Bad irq number or handler\n",
bttv_num);
goto fail1;
}
if (result==-EBUSY)
{
printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->irq);
goto fail1;
}
if (result < 0)
goto fail1;
if (0 != bttv_handle_chipset(btv)) {
result = -1;
goto fail2;
}
pci_set_master(dev);
pci_set_drvdata(dev,btv);
if(init_bt848(btv) < 0) {
bttv_remove(dev);
return -EIO;
}
bttv_num++;
return 0;
fail2:
free_irq(btv->irq,btv);
fail1:
release_mem_region(pci_resource_start(btv->dev,0),
pci_resource_len(btv->dev,0));
return result;
}
static struct pci_device_id bttv_pci_tbl[] __devinitdata = {
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{0,}
};
MODULE_DEVICE_TABLE(pci, bttv_pci_tbl);
static struct pci_driver bttv_pci_driver = {
name: "bttv",
id_table: bttv_pci_tbl,
probe: bttv_probe,
remove: bttv_remove,
};
int bttv_init_module(void)
{
bttv_num = 0;
printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n",
(BTTV_VERSION_CODE >> 16) & 0xff,
(BTTV_VERSION_CODE >> 8) & 0xff,
BTTV_VERSION_CODE & 0xff);
if (gbuffers < 2 || gbuffers > MAX_GBUFFERS)
gbuffers = 2;
if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF)
gbufsize = BTTV_MAX_FBUF;
if (bttv_verbose)
printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n",
gbuffers,gbufsize/1024,gbuffers*gbufsize/1024);
bttv_check_chipset();
return pci_module_init(&bttv_pci_driver);
}
void bttv_cleanup_module(void)
{
pci_unregister_driver(&bttv_pci_driver);
return;
}
module_init(bttv_init_module);
module_exit(bttv_cleanup_module);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/