📄 bttv-driver.c
字号:
/* 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 <asm/segment.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;#elsestatic unsigned int bigendian=0;#endifstatic 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 MODULEstatic 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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -