32_bttv diff -up linux-2.4.26/drivers/media/video/Makefile linux/drivers/media/video/Makefile --- linux-2.4.26/drivers/media/video/Makefile 2004-04-21 14:11:33.000000000 +0200 +++ linux/drivers/media/video/Makefile 2004-04-21 14:11:41.000000000 +0200 @@ -26,17 +26,19 @@ O_TARGET := video.o # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. export-objs := i2c-old.o videodev.o bttv-if.o cpia.o \ - v4l2-common.o v4l1-compat.o + v4l2-common.o v4l1-compat.o video-buf.o \ + btcx-risc.o list-multi := bttv.o zoran.o -bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o +bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \ + bttv-risc.o bttv-vbi.o bttv-i2c.o zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o obj-$(CONFIG_VIDEO_DEV) += videodev.o v4l2-common.o v4l1-compat.o obj-$(CONFIG_BUS_I2C) += i2c-old.o obj-$(CONFIG_VIDEO_BT848) += bttv.o msp3400.o tvaudio.o \ - tda7432.o tda9875.o tda9887.o tuner.o + tda7432.o tda9875.o tda9887.o tuner.o video-buf.o btcx-risc.o obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o obj-$(CONFIG_VIDEO_ZR36120) += zoran.o i2c-old.o diff -up linux-2.4.26/drivers/media/video/bt848.h linux/drivers/media/video/bt848.h --- linux-2.4.26/drivers/media/video/bt848.h 2004-04-21 13:42:41.000000000 +0200 +++ linux/drivers/media/video/bt848.h 2004-04-21 14:11:41.000000000 +0200 @@ -158,6 +158,9 @@ #define BT848_ADC_C_SLEEP (1<<1) #define BT848_ADC_CRUSH (1<<0) +#define BT848_WC_UP 0x044 +#define BT848_WC_DOWN 0x078 + #define BT848_E_VTC 0x06C #define BT848_O_VTC 0x0EC #define BT848_VTC_HSFMT (1<<7) @@ -282,13 +285,16 @@ #define BT848_GPIO_DMA_CTL_FIFO_ENABLE (1<<0) #define BT848_I2C 0x110 +#define BT878_I2C_MODE (1<<7) +#define BT878_I2C_RATE (1<<6) +#define BT878_I2C_NOSTOP (1<<5) +#define BT878_I2C_NOSTART (1<<4) #define BT848_I2C_DIV (0xf<<4) #define BT848_I2C_SYNC (1<<3) #define BT848_I2C_W3B (1<<2) #define BT848_I2C_SCL (1<<1) #define BT848_I2C_SDA (1<<0) - #define BT848_RISC_STRT_ADD 0x114 #define BT848_GPIO_OUT_EN 0x118 #define BT848_GPIO_REG_INP 0x11C diff -up linux-2.4.26/drivers/media/video/btcx-risc.c linux/drivers/media/video/btcx-risc.c --- linux-2.4.26/drivers/media/video/btcx-risc.c 2004-04-21 14:11:41.000000000 +0200 +++ linux/drivers/media/video/btcx-risc.c 2004-04-21 14:11:41.000000000 +0200 @@ -0,0 +1,257 @@ +/* + btcx-risc.c + + bt848/bt878/cx2388x risc code generator. + + (c) 2000-03 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs] + + 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/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <linux/videodev2.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +#include "btcx-risc.h" + +MODULE_DESCRIPTION("some code shared by bttv and cx88xx drivers"); +MODULE_AUTHOR("Gerd Knorr"); +MODULE_LICENSE("GPL"); + +static unsigned int debug = 0; +MODULE_PARM(debug,"i"); +MODULE_PARM_DESC(debug,"debug messages, default is 0 (no)"); + +/* ---------------------------------------------------------- */ +/* allocate/free risc memory */ + +static int memcnt; + +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc) +{ + if (NULL == risc->cpu) + return; + pci_free_consistent(pci, risc->size, risc->cpu, risc->dma); + memset(risc,0,sizeof(*risc)); + if (debug) { + memcnt--; + printk("btcx: riscmem free [%d]\n",memcnt); + } +} + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size) +{ + u32 *cpu; + dma_addr_t dma; + + if (NULL != risc->cpu && risc->size < size) + btcx_riscmem_free(pci,risc); + if (NULL == risc->cpu) { + cpu = pci_alloc_consistent(pci, size, &dma); + if (NULL == cpu) + return -ENOMEM; + risc->cpu = cpu; + risc->dma = dma; + risc->size = size; + if (debug) { + memcnt++; + printk("btcx: riscmem alloc size=%d [%d]\n",size,memcnt); + } + } + memset(risc->cpu,0,risc->size); + return 0; +} + +/* ---------------------------------------------------------- */ +/* screen overlay helpers */ + +int +btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n) +{ + if (win->left < 0) { + /* left */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = -win->left; + clips[n].c.height = win->height; + n++; + } + if (win->left + win->width > swidth) { + /* right */ + clips[n].c.left = swidth - win->left; + clips[n].c.top = 0; + clips[n].c.width = win->width - clips[n].c.left; + clips[n].c.height = win->height; + n++; + } + if (win->top < 0) { + /* top */ + clips[n].c.left = 0; + clips[n].c.top = 0; + clips[n].c.width = win->width; + clips[n].c.height = -win->top; + n++; + } + if (win->top + win->height > sheight) { + /* bottom */ + clips[n].c.left = 0; + clips[n].c.top = sheight - win->top; + clips[n].c.width = win->width; + clips[n].c.height = win->height - clips[n].c.top; + n++; + } + return n; +} + +int +btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, unsigned int n, int mask) +{ + s32 nx,nw,dx; + unsigned int i; + + /* fixup window */ + nx = (win->left + mask) & ~mask; + nw = (win->width) & ~mask; + if (nx + nw > win->left + win->width) + nw -= mask+1; + dx = nx - win->left; + win->left = nx; + win->width = nw; + if (debug) + printk(KERN_DEBUG "btcx: window align %dx%d+%d+%d [dx=%d]\n", + win->width, win->height, win->left, win->top, dx); + + /* fixup clips */ + for (i = 0; i < n; i++) { + nx = (clips[i].c.left-dx) & ~mask; + nw = (clips[i].c.width) & ~mask; + if (nx + nw < clips[i].c.left-dx + clips[i].c.width) + nw += mask+1; + clips[i].c.left = nx; + clips[i].c.width = nw; + if (debug) + printk(KERN_DEBUG "btcx: clip align %dx%d+%d+%d\n", + clips[i].c.width, clips[i].c.height, + clips[i].c.left, clips[i].c.top); + } + return 0; +} + +void +btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips) +{ + struct v4l2_clip swap; + int i,j,n; + + if (nclips < 2) + return; + for (i = nclips-2; i >= 0; i--) { + for (n = 0, j = 0; j <= i; j++) { + if (clips[j].c.left > clips[j+1].c.left) { + swap = clips[j]; + clips[j] = clips[j+1]; + clips[j+1] = swap; + n++; + } + } + if (0 == n) + break; + } +} + +void +btcx_calc_skips(int line, int width, unsigned int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips) +{ + unsigned int clip,skip; + int end,maxline; + + skip=0; + maxline = 9999; + for (clip = 0; clip < nclips; clip++) { + + /* sanity checks */ + if (clips[clip].c.left + clips[clip].c.width <= 0) + continue; + if (clips[clip].c.left > (signed)width) + break; + + /* vertical range */ + if (line > clips[clip].c.top+clips[clip].c.height-1) + continue; + if (line < clips[clip].c.top) { + if (maxline > clips[clip].c.top-1) + maxline = clips[clip].c.top-1; + continue; + } + if (maxline > clips[clip].c.top+clips[clip].c.height-1) + maxline = clips[clip].c.top+clips[clip].c.height-1; + + /* horizontal range */ + if (0 == skip || clips[clip].c.left > skips[skip-1].end) { + /* new one */ + skips[skip].start = clips[clip].c.left; + if (skips[skip].start < 0) + skips[skip].start = 0; + skips[skip].end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip].end > width) + skips[skip].end = width; + skip++; + } else { + /* overlaps -- expand last one */ + end = clips[clip].c.left + clips[clip].c.width; + if (skips[skip-1].end < end) + skips[skip-1].end = end; + if (skips[skip-1].end > width) + skips[skip-1].end = width; + } + } + *nskips = skip; + *maxy = maxline; + + if (debug) { + printk(KERN_DEBUG "btcx: skips line %d-%d:",line,maxline); + for (skip = 0; skip < *nskips; skip++) { + printk(" %d-%d",skips[skip].start,skips[skip].end); + } + printk("\n"); + } +} + +/* ---------------------------------------------------------- */ + +EXPORT_SYMBOL(btcx_riscmem_alloc); +EXPORT_SYMBOL(btcx_riscmem_free); + +EXPORT_SYMBOL(btcx_screen_clips); +EXPORT_SYMBOL(btcx_align); +EXPORT_SYMBOL(btcx_sort_clips); +EXPORT_SYMBOL(btcx_calc_skips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -up linux-2.4.26/drivers/media/video/btcx-risc.h linux/drivers/media/video/btcx-risc.h --- linux-2.4.26/drivers/media/video/btcx-risc.h 2004-04-21 14:11:41.000000000 +0200 +++ linux/drivers/media/video/btcx-risc.h 2004-04-21 14:11:41.000000000 +0200 @@ -0,0 +1,33 @@ + +struct btcx_riscmem { + unsigned int size; + u32 *cpu; + u32 *jmp; + dma_addr_t dma; +}; + +struct btcx_skiplist { + int start; + int end; +}; + +int btcx_riscmem_alloc(struct pci_dev *pci, + struct btcx_riscmem *risc, + unsigned int size); +void btcx_riscmem_free(struct pci_dev *pci, + struct btcx_riscmem *risc); + +int btcx_screen_clips(int swidth, int sheight, struct v4l2_rect *win, + struct v4l2_clip *clips, unsigned int n); +int btcx_align(struct v4l2_rect *win, struct v4l2_clip *clips, + unsigned int n, int mask); +void btcx_sort_clips(struct v4l2_clip *clips, unsigned int nclips); +void btcx_calc_skips(int line, int width, unsigned int *maxy, + struct btcx_skiplist *skips, unsigned int *nskips, + const struct v4l2_clip *clips, unsigned int nclips); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -up linux-2.4.26/drivers/media/video/bttv-cards.c linux/drivers/media/video/bttv-cards.c --- linux-2.4.26/drivers/media/video/bttv-cards.c 2004-04-21 13:44:00.000000000 +0200 +++ linux/drivers/media/video/bttv-cards.c 2004-04-21 14:11:41.000000000 +0200 @@ -53,8 +53,11 @@ static void winview_audio(struct bttv *b static void lt9415_audio(struct bttv *btv, struct video_audio *v, int set); static void avermedia_tvphone_audio(struct bttv *btv, struct video_audio *v, int set); +static void avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v, + int set); static void terratv_audio(struct bttv *btv, struct video_audio *v, int set); static void gvbctv3pci_audio(struct bttv *btv, struct video_audio *v, int set); +static void gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set); static void winfast2000_audio(struct bttv *btv, struct video_audio *v, int set); static void pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set); static void fv2000s_audio(struct bttv *btv, struct video_audio *v, int set); @@ -63,6 +66,10 @@ static void adtvk503_audio(struct bttv * static void rv605_muxsel(struct bttv *btv, unsigned int input); static void eagle_muxsel(struct bttv *btv, unsigned int input); static void xguard_muxsel(struct bttv *btv, unsigned int input); +static void ivc120_muxsel(struct bttv *btv, unsigned int input); +static void gvc1100_muxsel(struct bttv *btv, unsigned int input); + +static void PXC200_muxsel(struct bttv *btv, unsigned int input); static int terratec_active_radio_upgrade(struct bttv *btv); static int tea5757_read(struct bttv *btv); @@ -76,10 +83,11 @@ static unsigned int vsfx=0; static unsigned int latency = UNSET; unsigned int no_overlay=-1; -static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET}; -static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET}; -static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET}; -static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET}; +static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; +static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; +static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; +static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; +static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET }; #ifdef MODULE static unsigned int autoload = 1; #else @@ -107,9 +115,13 @@ MODULE_PARM(tuner,"1-" __stringify(BTTV_ MODULE_PARM_DESC(tuner,"specify installed tuner type"); MODULE_PARM(autoload,"i"); MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); + +MODULE_PARM(svhs,"1-" __stringify(BTTV_MAX) "i"); +MODULE_PARM(remote,"1-" __stringify(BTTV_MAX) "i"); + MODULE_PARM(gpiomask,"i"); MODULE_PARM(audioall,"i"); -MODULE_PARM(audiomux,"1-5i"); +MODULE_PARM(audiomux,"1-6i"); /* kernel args */ #ifndef MODULE @@ -154,12 +166,13 @@ static struct CARD { { 0x00031002, BTTV_ATI_TVWONDERVE,"ATI TV Wonder/VE" }, { 0x6606107d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, - { 0x6607107d, BTTV_WINFAST2000, "Leadtek WinFast VC 100" }, + { 0x6607107d, BTTV_WINFASTVC100, "Leadtek WinFast VC 100" }, { 0x263610b4, BTTV_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, { 0x264510b4, BTTV_STB2, "STB TV PCI FM, Gateway P/N 6000704" }, { 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" }, { 0x405010fc, BTTV_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" }, { 0x407010fc, BTTV_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" }, + { 0xd01810fc, BTTV_GVBCTV5PCI, "I-O Data Co. GV-BCTV5/PCI" }, { 0x001211bd, BTTV_PINNACLE, "Pinnacle PCTV" }, { 0x001c11bd, BTTV_PINNACLESAT, "Pinnacle PCTV Sat" }, @@ -174,16 +187,15 @@ static struct CARD { { 0x3002144f, BTTV_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH05x" }, { 0x3005144f, BTTV_MAGICTVIEW061, "(Askey Magic/others) TView99 CPH061/06L (T1/LC)" }, { 0x5000144f, BTTV_MAGICTVIEW061, "Askey CPH050" }, - + { 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" }, + { 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" }, + { 0x00011461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, { 0x00021461, BTTV_AVERMEDIA98, "AVermedia TVCapture 98" }, { 0x00031461, BTTV_AVPHONE98, "AVerMedia TVPhone98" }, { 0x00041461, BTTV_AVERMEDIA98, "AVerMedia TVCapture 98" }, { 0x03001461, BTTV_AVERMEDIA98, "VDOMATE TV TUNER CARD" }, - { 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" }, - { 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" }, - { 0x1117153b, BTTV_TERRATVALUE, "Terratec TValue (Philips PAL B/G)" }, { 0x1118153b, BTTV_TERRATVALUE, "Terratec TValue (Temic PAL B/G)" }, { 0x1119153b, BTTV_TERRATVALUE, "Terratec TValue (Philips PAL I)" }, @@ -195,7 +207,8 @@ static struct CARD { //{ 0x18521852, BTTV_TERRATV, "Terratec TV+ (V1.10)" }, { 0x1134153b, BTTV_TERRATVALUE, "Terratec TValue (LR102)" }, { 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" }, // LR102 - { 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" }, // ?? + { 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" }, // ?? + { 0xff3b153b, BTTV_TERRATVALUER, "Terratec TValue Radio" }, // ?? { 0x400015b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, { 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" }, @@ -218,14 +231,31 @@ static struct CARD { { 0x1466aa06, BTTV_PV150, "Provideo PV150B-3" }, { 0x1467aa07, BTTV_PV150, "Provideo PV150B-4" }, - { 0xa1550000, BTTV_IVC200, "IVC-200" }, - { 0xa1550001, BTTV_IVC200, "IVC-200" }, - { 0xa1550002, BTTV_IVC200, "IVC-200" }, - { 0xa1550003, BTTV_IVC200, "IVC-200" }, + { 0xa132ff00, BTTV_IVC100, "IVC-100" }, + { 0xa1550000, BTTV_IVC200, "IVC-200" }, + { 0xa1550001, BTTV_IVC200, "IVC-200" }, + { 0xa1550002, BTTV_IVC200, "IVC-200" }, + { 0xa1550003, BTTV_IVC200, "IVC-200" }, { 0xa1550100, BTTV_IVC200, "IVC-200G" }, { 0xa1550101, BTTV_IVC200, "IVC-200G" }, { 0xa1550102, BTTV_IVC200, "IVC-200G" }, { 0xa1550103, BTTV_IVC200, "IVC-200G" }, + { 0xa182ff00, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff01, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff02, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff03, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff04, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff05, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff06, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff07, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff08, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff09, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff0a, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff0b, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff0c, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff0d, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff0e, BTTV_IVC120, "IVC-120G" }, + { 0xa182ff0f, BTTV_IVC120, "IVC-120G" }, { 0x41424344, BTTV_GRANDTEC, "GrandTec Multi Capture" }, { 0x01020304, BTTV_XGUARD, "Grandtec Grand X-Guard" }, @@ -235,16 +265,30 @@ static struct CARD { { 0x18501851, BTTV_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" }, { 0x18511851, BTTV_FLYVIDEO98EZ, "FlyVideo 98EZ (LR51)/ CyberMail AV" }, { 0x18521852, BTTV_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" }, + { 0x41a0a051, BTTV_FLYVIDEO_98FM, "Lifeview FlyVideo 98 LR50 Rev Q" }, { 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" }, { 0x217d6606, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, + { 0xfff6f6ff, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, { 0x03116000, BTTV_SENSORAY311, "Sensoray 311" }, { 0x00790e11, BTTV_WINDVR, "Canopus WinDVR PCI" }, { 0xa0fca1a0, BTTV_ZOLTRIX, "Face to Face Tvmax" }, - { 0x01010071, BTTV_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, + { 0x20007063, BTTV_PC_HDTV, "pcHDTV HD-2000 TV"}, + { 0x82b2aa6a, BTTV_SIMUS_GVC1100, "SIMUS GVC1100" }, + { 0x146caa0c, BTTV_PV951, "ituner spectra8" }, + { 0x200a1295, BTTV_PXC200, "ImageNation PXC200A" }, + + { 0x40111554, BTTV_PV_BT878P_9B, "Prolink Pixelview PV-BT" }, + { 0x17de0a01, BTTV_KWORLD, "Mecer TV/FM/Video Tuner" }, // likely broken, vendor id doesn't match the other magic views ... //{ 0xa0fca04f, BTTV_MAGICTVIEW063, "Guillemot Maxi TV Video 3" }, + // DVB cards (using pci function .1 for mpeg data xfer) + { 0x01010071, BTTV_NEBULA_DIGITV, "Nebula Electronics DigiTV" }, + { 0x002611bd, BTTV_TWINHAN_DST, "Pinnacle PCTV SAT CI" }, + { 0x00011822, BTTV_TWINHAN_DST, "Twinhan VisionPlus DVB-T" }, + { 0xfc00270f, BTTV_TWINHAN_DST, "ChainTech digitop DST-1000 DVB-S" }, + { 0, -1, NULL } }; @@ -334,6 +378,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .tuner_type = -1, .audio_hook = avermedia_tvphone_audio, + .has_remote = 1, },{ .name = "MATRIX-Vision MV-Delta", .video_inputs = 5, @@ -420,6 +465,7 @@ struct tvcard bttv_tvcards[] = { .msp34xx_alt = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL, + .audio_hook = avermedia_tv_stereo_audio, },{ .name = "Aimslab Video Highway Xtreme (VHX)", .video_inputs = 3, @@ -453,7 +499,13 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x01fe00, .muxsel = { 2, 3, 1, 1}, +#if 0 + // old .audiomux = { 0x01c000, 0, 0x018000, 0x014000, 0x002000, 0 }, +#else + // 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> + .audiomux = { 0x001e00, 0, 0x018000, 0x014000, 0x002000, 0 }, +#endif .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, @@ -552,6 +604,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, + .has_remote = 1, },{ .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar", .video_inputs = 3, @@ -643,6 +696,8 @@ struct tvcard bttv_tvcards[] = { .audiomux = { 0 }, .needs_tvaudio = 1, .tuner_type = -1, + .muxsel_hook = PXC200_muxsel, + },{ .name = "Lifeview FlyVideo 98 LR50", .video_inputs = 4, @@ -696,9 +751,15 @@ struct tvcard bttv_tvcards[] = { .audio_inputs = 1, .tuner = 0, .svhs = 2, - .gpiomask = 0xc33000, .muxsel = { 2, 3, 1, 1, 0}, // TV, CVid, SVid, CVid over SVid connector - .audiomux = { 0x422000,0x1000,0x0000,0x620000,0x800000}, +#if 0 + .gpiomask = 0xc33000, + .audiomux = { 0x422000,0x1000,0x0000,0x620000,0x800000 }, +#else + /* Alexander Varakin <avarakin@hotmail.com> [stereo version] */ + .gpiomask = 0xb33000, + .audiomux = { 0x122000,0x1000,0x0000,0x620000,0x800000 }, +#endif /* Audio Routing for "WinFast 2000 XP" (no tv stereo !) gpio23 -- hef4052:nEnable (0x800000) gpio12 -- hef4052:A1 @@ -715,6 +776,7 @@ struct tvcard bttv_tvcards[] = { .has_radio = 1, .tuner_type = 5, // default for now, gpio reads BFFF06 for Pal bg+dk .audio_hook = winfast2000_audio, + .has_remote = 1, },{ .name = "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II", .video_inputs = 4, @@ -764,6 +826,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 1, + .has_remote = 1, },{ .name = "Pinnacle PCTV Studio/Rave", .video_inputs = 3, @@ -772,8 +835,8 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x03000F, .muxsel = { 2, 3, 1, 1}, - .audiomux = { 2, 0, 0, 0, 1}, - .needs_tvaudio = 1, + .audiomux = { 2, 0xd0001, 0, 0, 1}, + .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = -1, },{ @@ -922,6 +985,7 @@ struct tvcard bttv_tvcards[] = { .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_PAL_I, + .has_remote = 1, /* GPIO wiring: (different from Rev.4C !) GPIO17: U4.A0 (first hef4052bt) GPIO19: U4.A1 @@ -963,14 +1027,14 @@ struct tvcard bttv_tvcards[] = { MUX2 (mask 0x30000): 0,2,3= from MSP34xx 1= FM stereo Radio from Tuner */ - .needs_tvaudio = 1, + .needs_tvaudio = 0, .pll = PLL_28, .tuner_type = -1, },{ /* Claas Langbehn <claas@bigfoot.com>, Sven Grothklags <sven@upb.de> */ .name = "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS", - .video_inputs = 3, + .video_inputs = 4, .audio_inputs = 3, .tuner = 0, .svhs = 2, @@ -979,7 +1043,7 @@ struct tvcard bttv_tvcards[] = { .audiomux = { 0, 0, 0x10, 8, 4 }, .needs_tvaudio = 1, .pll = PLL_28, - .tuner_type = TUNER_PHILIPS_PAL_I, + .tuner_type = TUNER_PHILIPS_PAL, .has_radio = 1, },{ /* Tim Röstermundt <rosterm@uni-muenster.de> @@ -1230,6 +1294,7 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = 25, + .has_remote = 1, /* GPIO wiring: GPIO0: U4.A0 (hef4052bt) GPIO1: U4.A1 @@ -1265,6 +1330,7 @@ struct tvcard bttv_tvcards[] = { .tuner_type = 5, .audio_hook = pvbt878p9b_audio, // Note: not all cards have stereo .has_radio = 1, // Note: not all cards have radio + .has_remote = 1, /* GPIO wiring: GPIO0: A0 hef4052 GPIO1: A1 hef4052 @@ -1400,11 +1466,11 @@ struct tvcard bttv_tvcards[] = { .svhs = 2, .gpiomask = 0x0f0f80, .muxsel = {2, 3, 1, 0}, - .audiomux = {0x030000, 0x010000, 0x030000, 0, 0x020000, 0}, + .audiomux = {0x030000, 0x010000, 0, 0, 0x020000, 0}, .no_msp34xx = 1, .pll = PLL_28, .tuner_type = TUNER_PHILIPS_NTSC_M, - .audio_hook = gvbctv3pci_audio, + .audio_hook = gvbctv5pci_audio, .has_radio = 1, },{ .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */ @@ -1726,16 +1792,17 @@ struct tvcard bttv_tvcards[] = { /* ---- card 0x68 ---------------------------------- */ .name = "Nebula Electronics DigiTV", - .video_inputs = 0, - .audio_inputs = 0, + .video_inputs = 1, + .tuner = -1, .svhs = -1, .muxsel = { 2, 3, 1, 0}, - .needs_tvaudio = 0, .no_msp34xx = 1, .no_tda9875 = 1, .no_tda7432 = 1, .pll = PLL_28, .tuner_type = -1, + .has_dvb = 1, + .no_gpioirq = 1, },{ /* Jorge Boncompte - DTI2 <jorge@dti2.net> */ .name = "ProVideo PV143", @@ -1807,6 +1874,144 @@ struct tvcard bttv_tvcards[] = { .needs_tvaudio = 1, .pll = PLL_28, .tuner_type = -1, +},{ + .name = "IVC-100", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .tuner_type = -1, + .svhs = -1, + .gpiomask = 0xdf, + .muxsel = { 2, 3, 1, 0 }, + .pll = PLL_28, +},{ + /* IVC-120G - Alan Garfield <alan@fromorbit.com> */ + .name = "IVC-120G", + .video_inputs = 16, + .audio_inputs = 0, /* card has no audio */ + .tuner = -1, /* card has no tuner */ + .tuner_type = -1, + .svhs = -1, /* card has no svhs */ + .needs_tvaudio = 0, + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .gpiomask = 0x00, + .muxsel = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, + .muxsel_hook = ivc120_muxsel, + .pll = PLL_28, +},{ + + /* ---- card 0x70 ---------------------------------- */ + .name = "pcHDTV HD-2000 TV", + .video_inputs = 4, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .muxsel = { 2, 3, 1, 0}, + .tuner_type = TUNER_PHILIPS_ATSC, +},{ + .name = "Twinhan DST + clones", + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .tuner_type = TUNER_ABSENT, + .no_video = 1, + .has_dvb = 1, +},{ + .name = "Winfast VC100", + .video_inputs = 3, + .audio_inputs = 0, + .svhs = 1, + .tuner = -1, // no tuner + .muxsel = { 3, 1, 1, 3}, // Vid In, SVid In, Vid over SVid in connector + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .tuner_type = TUNER_ABSENT, + .no_video = 1, + .pll = PLL_28, +},{ + .name = "Teppro TEV-560/InterVision IV-560", + .video_inputs = 3, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .gpiomask = 3, + .muxsel = { 2, 3, 1, 1}, + .audiomux = { 1, 1, 1, 1, 0}, + .needs_tvaudio = 1, + .tuner_type = TUNER_PHILIPS_PAL, + .pll = PLL_35, +},{ + + /* ---- card 0x74 ---------------------------------- */ + .name = "SIMUS GVC1100", + .video_inputs = 4, + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .tuner_type = -1, + .pll = PLL_28, + .muxsel = { 2, 2, 2, 2}, + .gpiomask = 0x3F, + .muxsel_hook = gvc1100_muxsel, +},{ + /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */ + .name = "NGS NGSTV+", + .video_inputs = 3, + .tuner = 0, + .svhs = 2, + .gpiomask = 0x008007, + .muxsel = {2, 3, 0, 0}, + .audiomux = {0, 0, 0, 0, 0x000003, 0}, + .pll = PLL_28, + .tuner_type = TUNER_PHILIPS_PAL, + .has_remote = 1, +},{ + /* http://linuxmedialabs.com */ + .name = "LMLBT4", + .video_inputs = 4, /* IN1,IN2,IN3,IN4 */ + .audio_inputs = 0, + .tuner = -1, + .svhs = -1, + .muxsel = { 2, 3, 1, 0 }, + .no_msp34xx = 1, + .no_tda9875 = 1, + .no_tda7432 = 1, + .needs_tvaudio = 0, +},{ + /* Helmroos Harri <harri.helmroos@pp.inet.fi> */ + .name = "Tekram M205 PRO", + .video_inputs = 3, + .audio_inputs = 1, + .tuner = 0, + .tuner_type = TUNER_PHILIPS_PAL, + .svhs = 2, + .needs_tvaudio = 0, + .gpiomask = 0x68, + .muxsel = { 2, 3, 1}, + .audiomux = { 0x68, 0x68, 0x61, 0x61, 0x00 }, + .pll = PLL_28, +},{ + + /* ---- card 0x78 ---------------------------------- */ + /* Javier Cendan Ares <jcendan@lycos.es> */ + /* bt878 TV + FM without subsystem ID */ + .name = "Conceptronic CONTVFMi", + .video_inputs = 3, + .audio_inputs = 1, + .tuner = 0, + .svhs = 2, + .gpiomask = 0x008007, + .muxsel = { 2, 3, 1, 1 }, + .audiomux = { 0, 1, 2, 2, 3 }, + .needs_tvaudio = 0, + .pll = PLL_28, + .tuner_type = TUNER_PHILIPS_PAL, + .has_remote = 1, + .has_radio = 1, }}; const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards); @@ -1825,9 +2030,9 @@ void __devinit bttv_idcard(struct bttv * unsigned short tmp; /* read PCI subsystem ID */ - pci_read_config_word(btv->dev, PCI_SUBSYSTEM_ID, &tmp); + pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_ID, &tmp); btv->cardid = tmp << 16; - pci_read_config_word(btv->dev, PCI_SUBSYSTEM_VENDOR_ID, &tmp); + pci_read_config_word(btv->c.pci, PCI_SUBSYSTEM_VENDOR_ID, &tmp); btv->cardid |= tmp; if (0 != btv->cardid && 0xffffffff != btv->cardid) { @@ -1840,14 +2045,14 @@ void __devinit bttv_idcard(struct bttv * /* found it */ printk(KERN_INFO "bttv%d: detected: %s [card=%d], " "PCI subsystem ID is %04x:%04x\n", - btv->nr,cards[type].name,cards[type].cardnr, + btv->c.nr,cards[type].name,cards[type].cardnr, btv->cardid & 0xffff, (btv->cardid >> 16) & 0xffff); - btv->type = cards[type].cardnr; + btv->c.type = cards[type].cardnr; } else { /* 404 */ printk(KERN_INFO "bttv%d: subsystem: %04x:%04x (UNKNOWN)\n", - btv->nr, btv->cardid & 0xffff, + btv->c.nr, btv->cardid & 0xffff, (btv->cardid >> 16) & 0xffff); printk(KERN_DEBUG "please mail id, board name and " "the correct card= insmod option to kraxel@bytesex.org\n"); @@ -1855,13 +2060,13 @@ void __devinit bttv_idcard(struct bttv * } /* let the user override the autodetected type */ - if (card[btv->nr] < bttv_num_tvcards) - btv->type=card[btv->nr]; + if (card[btv->c.nr] < bttv_num_tvcards) + btv->c.type=card[btv->c.nr]; /* print which card config we are using */ - printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->nr, - bttv_tvcards[btv->type].name, btv->type, - card[btv->nr] < bttv_num_tvcards + printk(KERN_INFO "bttv%d: using: %s [card=%d,%s]\n",btv->c.nr, + bttv_tvcards[btv->c.type].name, btv->c.type, + card[btv->c.nr] < bttv_num_tvcards ? "insmod option" : "autodetected"); /* overwrite gpio stuff ?? */ @@ -1871,20 +2076,20 @@ void __devinit bttv_idcard(struct bttv * if (UNSET != audiomux[0]) { gpiobits = 0; for (i = 0; i < 5; i++) { - bttv_tvcards[btv->type].audiomux[i] = audiomux[i]; + bttv_tvcards[btv->c.type].audiomux[i] = audiomux[i]; gpiobits |= audiomux[i]; } } else { gpiobits = audioall; for (i = 0; i < 5; i++) { - bttv_tvcards[btv->type].audiomux[i] = audioall; + bttv_tvcards[btv->c.type].audiomux[i] = audioall; } } - bttv_tvcards[btv->type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; + bttv_tvcards[btv->c.type].gpiomask = (UNSET != gpiomask) ? gpiomask : gpiobits; printk(KERN_INFO "bttv%d: gpio config override: mask=0x%x, mux=", - btv->nr,bttv_tvcards[btv->type].gpiomask); + btv->c.nr,bttv_tvcards[btv->c.type].gpiomask); for (i = 0; i < 5; i++) { - printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->type].audiomux[i]); + printk("%s0x%x", i ? "," : "", bttv_tvcards[btv->c.type].audiomux[i]); } printk("\n"); } @@ -1898,7 +2103,7 @@ void identify_by_eeprom(struct bttv *btv { int type = -1; - if (0 == strncmp(eeprom_data,"GET.MM20xPCTV",13)) + if (0 == strncmp(eeprom_data,"GET MM20xPCTV",13)) type = BTTV_MODTEC_205; else if (0 == strncmp(eeprom_data+20,"Picolo",7)) type = BTTV_EURESYS_PICOLO; @@ -1906,22 +2111,22 @@ void identify_by_eeprom(struct bttv *btv type = BTTV_HAUPPAUGE; /* old bt848 */ if (-1 != type) { - btv->type = type; + btv->c.type = type; printk("bttv%d: detected by eeprom: %s [card=%d]\n", - btv->nr, bttv_tvcards[btv->type].name, btv->type); + btv->c.nr, bttv_tvcards[btv->c.type].name, btv->c.type); } } static void flyvideo_gpio(struct bttv *btv) { - int gpio,outbits,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821; + int gpio,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821; int tuner=-1,ttype; - - outbits = btread(BT848_GPIO_OUT_EN); - btwrite(0x00, BT848_GPIO_OUT_EN); + + gpio_inout(0xffffff, 0); udelay(8); // without this we would see the 0x1800 mask - gpio=btread(BT848_GPIO_DATA); - btwrite(outbits, BT848_GPIO_OUT_EN); + gpio = gpio_read(); + /* FIXME: must restore OUR_EN ??? */ + // all cards provide GPIO info, some have an additional eeprom // LR50: GPIO coding can be found lower right CP1 .. CP9 // CP9=GPIO23 .. CP1=GPIO15; when OPEN, the corresponding GPIO reads 1. @@ -1946,7 +2151,7 @@ static void flyvideo_gpio(struct bttv *b case 0xC: tuner=3; // Philips SECAM(+PAL) FQ1216ME or FI1216MF break; default: - printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->nr); + printk(KERN_INFO "bttv%d: FlyVideo_gpio: unknown tuner type.\n", btv->c.nr); } has_remote = gpio & 0x800000; @@ -1962,9 +2167,9 @@ static void flyvideo_gpio(struct bttv *b tuner=4; // No tuner present printk(KERN_INFO "bttv%d: FlyVideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n", - btv->nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); + btv->c.nr, has_radio? "yes":"no ", has_remote? "yes":"no ", tuner, gpio); printk(KERN_INFO "bttv%d: FlyVideo LR90=%s tda9821/tda9820=%s capture_only=%s\n", - btv->nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", + btv->c.nr, is_lr90?"yes":"no ", has_tda9820_tda9821?"yes":"no ", is_capture_only?"yes":"no "); if(tuner!= -1) // only set if known tuner autodetected, else let insmod option through @@ -1988,8 +2193,8 @@ static void miro_pinnacle_gpio(struct bt int id,msp,gpio; char *info; - btwrite(0,BT848_GPIO_OUT_EN); - gpio = btread(BT848_GPIO_DATA); + gpio_inout(0xffffff, 0); + gpio = gpio_read(); id = ((gpio>>10) & 63) -1; msp = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx"); if (id < 32) { @@ -2008,14 +2213,14 @@ static void miro_pinnacle_gpio(struct bt btv->has_radio = 0; } if (-1 != msp) { - if (btv->type == BTTV_MIRO) - btv->type = BTTV_MIROPRO; - if (btv->type == BTTV_PINNACLE) - btv->type = BTTV_PINNACLEPRO; + if (btv->c.type == BTTV_MIRO) + btv->c.type = BTTV_MIROPRO; + if (btv->c.type == BTTV_PINNACLE) + btv->c.type = BTTV_PINNACLEPRO; } printk(KERN_INFO "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n", - btv->nr, id+1, btv->tuner_type, + btv->c.nr, id+1, btv->tuner_type, !btv->has_radio ? "no" : (btv->has_matchbox ? "matchbox" : "fmtuner"), (-1 == msp) ? "no" : "yes"); @@ -2044,15 +2249,18 @@ static void miro_pinnacle_gpio(struct bt case 6: info = "NTSC / stereo"; break; + case 7: + info = "PAL / stereo"; + break; default: info = "oops: unknown card"; break; } if (-1 != msp) - btv->type = BTTV_PINNACLEPRO; + btv->c.type = BTTV_PINNACLEPRO; printk(KERN_INFO "bttv%d: pinnacle/mt: id=%d info=\"%s\" radio=%s\n", - btv->nr, id, info, btv->has_radio ? "yes" : "no"); + btv->c.nr, id, info, btv->has_radio ? "yes" : "no"); btv->tuner_type = 33; btv->pinnacle_id = id; } @@ -2063,16 +2271,14 @@ static void miro_pinnacle_gpio(struct bt static void init_ids_eagle(struct bttv *btv) { - btwrite(0xFFFF37, BT848_GPIO_OUT_EN); - btwrite(0x000000, BT848_GPIO_REG_INP); - - btwrite(0x200020, BT848_GPIO_DATA); + gpio_inout(0xffffff,0xFFFF37); + gpio_write(0x200020); /* flash strobe inverter ?! */ - btwrite(0x200024, BT848_GPIO_DATA); + gpio_write(0x200024); /* switch sync drive off */ - btor(LM1882_SYNC_DRIVE, BT848_GPIO_DATA); + gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE); /* set BT848 muxel to 2 */ btaor((2)<<5, ~(2<<5), BT848_IFORM); @@ -2084,8 +2290,7 @@ static void init_ids_eagle(struct bttv * static void eagle_muxsel(struct bttv *btv, unsigned int input) { btaor((2)<<5, ~(3<<5), BT848_IFORM); - btaor((bttv_tvcards[btv->type].muxsel[input&7]&3), - ~3, BT848_GPIO_DATA); + gpio_bits(3,bttv_tvcards[btv->c.type].muxsel[input&7]); #if 0 /* svhs */ @@ -2104,15 +2309,64 @@ static void eagle_muxsel(struct bttv *bt #endif /* switch sync drive off */ - btor(LM1882_SYNC_DRIVE, BT848_GPIO_DATA); + gpio_bits(LM1882_SYNC_DRIVE,LM1882_SYNC_DRIVE); +} + +static void gvc1100_muxsel(struct bttv *btv, unsigned int input) +{ + static const int masks[] = {0x30, 0x01, 0x12, 0x23}; + gpio_write(masks[input%4]); +} + +/* LMLBT4x initialization - to allow access to GPIO bits for sensors input and + alarms output + + GPIObit | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + assignment | TI | O3|INx| O2| O1|IN4|IN3|IN2|IN1| | | + + IN - sensor inputs, INx - sensor inputs and TI XORed together + O1,O2,O3 - alarm outputs (relays) + + OUT ENABLE 1 1 0 . 1 1 0 0 . 0 0 0 0 = 0x6C0 + +*/ + +static void init_lmlbt4x(struct bttv *btv) +{ + printk(KERN_DEBUG "LMLBT4x init\n"); + btwrite(0x000000, BT848_GPIO_REG_INP); + gpio_inout(0xffffff, 0x0006C0); + gpio_write(0x000000); } + /* ----------------------------------------------------------------------- */ +void bttv_reset_audio(struct bttv *btv) +{ + /* + * BT878A has a audio-reset register. + * 1. This register is an audio reset function but it is in + * function-0 (video capture) address space. + * 2. It is enough to do this once per power-up of the card. + * 3. There is a typo in the Conexant doc -- it is not at + * 0x5B, but at 0x058. (B is an odd-number, obviously a typo!). + * --//Shrikumar 030609 + */ + if (btv->id != 878) + return; + + if (bttv_debug) + printk("bttv%d: BT878A ARESET\n",btv->c.nr); + btwrite((1<<7), 0x058); + udelay(10); + btwrite( 0, 0x058); +} + /* initialization part one -- before registering i2c bus */ void __devinit bttv_init_card1(struct bttv *btv) { - switch (btv->type) { + switch (btv->c.type) { case BTTV_HAUPPAUGE: case BTTV_HAUPPAUGE878: boot_msp34xx(btv,5); @@ -2126,6 +2380,9 @@ void __devinit bttv_init_card1(struct bt case BTTV_HAUPPAUGEPVR: pvr_boot(btv); break; + case BTTV_TWINHAN_DST: + btv->use_i2c_hw = 1; + break; } } @@ -2134,12 +2391,12 @@ void __devinit bttv_init_card2(struct bt { btv->tuner_type = -1; - if (BTTV_UNKNOWN == btv->type) { + if (BTTV_UNKNOWN == btv->c.type) { bttv_readee(btv,eeprom_data,0xa0); identify_by_eeprom(btv,eeprom_data); } - switch (btv->type) { + switch (btv->c.type) { case BTTV_MIRO: case BTTV_MIROPRO: case BTTV_PINNACLE: @@ -2191,7 +2448,7 @@ void __devinit bttv_init_card2(struct bt case BTTV_MAGICTVIEW061: if (btv->cardid == 0x3002144f) { btv->has_radio=1; - printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->nr); + printk("bttv%d: radio detected by subsystem id (CPH05x)\n",btv->c.nr); } break; case BTTV_STB2: @@ -2223,21 +2480,24 @@ void __devinit bttv_init_card2(struct bt bttv_readee(btv,eeprom_data,0xa0); modtec_eeprom(btv); break; + case BTTV_LMLBT4: + init_lmlbt4x(btv); + break; } /* pll configuration */ if (!(btv->id==848 && btv->revision==0x11)) { /* defaults from card list */ - if (PLL_28 == bttv_tvcards[btv->type].pll) { + if (PLL_28 == bttv_tvcards[btv->c.type].pll) { btv->pll.pll_ifreq=28636363; btv->pll.pll_crystal=BT848_IFORM_XT0; } - if (PLL_35 == bttv_tvcards[btv->type].pll) { + if (PLL_35 == bttv_tvcards[btv->c.type].pll) { btv->pll.pll_ifreq=35468950; btv->pll.pll_crystal=BT848_IFORM_XT1; } /* insmod options can override */ - switch (pll[btv->nr]) { + switch (pll[btv->c.nr]) { case 0: /* none */ btv->pll.pll_crystal = 0; btv->pll.pll_ifreq = 0; @@ -2260,27 +2520,33 @@ void __devinit bttv_init_card2(struct bt btv->pll.pll_current = -1; /* tuner configuration (from card list / autodetect / insmod option) */ - if (UNSET != bttv_tvcards[btv->type].tuner_type) + if (UNSET != bttv_tvcards[btv->c.type].tuner_type) if(UNSET == btv->tuner_type) - btv->tuner_type = bttv_tvcards[btv->type].tuner_type; - if (UNSET != tuner[btv->nr]) - btv->tuner_type = tuner[btv->nr]; - printk("bttv%d: using tuner=%d\n",btv->nr,btv->tuner_type); + btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type; + if (UNSET != tuner[btv->c.nr]) + btv->tuner_type = tuner[btv->c.nr]; + printk("bttv%d: using tuner=%d\n",btv->c.nr,btv->tuner_type); if (btv->pinnacle_id != UNSET) bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE, &btv->pinnacle_id); if (btv->tuner_type != UNSET) bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); - btv->svhs = bttv_tvcards[btv->type].svhs; - if (svhs[btv->nr] != UNSET) - btv->svhs = svhs[btv->nr]; + btv->svhs = bttv_tvcards[btv->c.type].svhs; + if (svhs[btv->c.nr] != UNSET) + btv->svhs = svhs[btv->c.nr]; + if (remote[btv->c.nr] != UNSET) + btv->has_remote = remote[btv->c.nr]; - if (bttv_tvcards[btv->type].has_radio) + if (bttv_tvcards[btv->c.type].has_radio) btv->has_radio=1; - if (bttv_tvcards[btv->type].audio_hook) - btv->audio_hook=bttv_tvcards[btv->type].audio_hook; + if (bttv_tvcards[btv->c.type].has_remote) + btv->has_remote=1; + if (bttv_tvcards[btv->c.type].no_gpioirq) + btv->gpioirq=0; + if (bttv_tvcards[btv->c.type].audio_hook) + btv->audio_hook=bttv_tvcards[btv->c.type].audio_hook; - if (bttv_tvcards[btv->type].digital_mode == DIGITAL_MODE_CAMERA) { + if (bttv_tvcards[btv->c.type].digital_mode == DIGITAL_MODE_CAMERA) { /* detect Bt832 chip for quartzsight digital camera */ if ((bttv_I2CRead(btv, I2C_BT832_ALT1, "Bt832") >=0) || (bttv_I2CRead(btv, I2C_BT832_ALT2, "Bt832") >=0)) @@ -2288,31 +2554,31 @@ void __devinit bttv_init_card2(struct bt } /* try to detect audio/fader chips */ - if (!bttv_tvcards[btv->type].no_msp34xx && + if (!bttv_tvcards[btv->c.type].no_msp34xx && bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) { if (autoload) request_module("msp3400"); } - if (bttv_tvcards[btv->type].msp34xx_alt && + if (bttv_tvcards[btv->c.type].msp34xx_alt && bttv_I2CRead(btv, I2C_MSP3400_ALT, "MSP34xx (alternate address)") >=0) { if (autoload) request_module("msp3400"); } - if (!bttv_tvcards[btv->type].no_tda9875 && + if (!bttv_tvcards[btv->c.type].no_tda9875 && bttv_I2CRead(btv, I2C_TDA9875, "TDA9875") >=0) { if (autoload) request_module("tda9875"); } - if (!bttv_tvcards[btv->type].no_tda7432 && + if (!bttv_tvcards[btv->c.type].no_tda7432 && bttv_I2CRead(btv, I2C_TDA7432, "TDA7432") >=0) { if (autoload) request_module("tda7432"); } - if (bttv_tvcards[btv->type].needs_tvaudio) { + if (bttv_tvcards[btv->c.type].needs_tvaudio) { if (autoload) request_module("tvaudio"); } @@ -2375,8 +2641,8 @@ hauppauge_tuner[] __devinitdata = { TUNER_TEMIC_4039FR5_NTSC, "Temic 4039FR5" }, { TUNER_PHILIPS_FQ1216ME, "Philips FQ1216 ME" }, { TUNER_TEMIC_4066FY5_PAL_I, "Temic 4066FY5" }, - { TUNER_ABSENT, "Philips TD1536" }, - { TUNER_ABSENT, "Philips TD1536D" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536" }, + { TUNER_PHILIPS_NTSC, "Philips TD1536D" }, { TUNER_PHILIPS_NTSC, "Philips FMR1236" }, /* mono radio */ { TUNER_ABSENT, "Philips FI1256MP" }, { TUNER_ABSENT, "Samsung TCPQ9091P" }, @@ -2396,11 +2662,15 @@ static void modtec_eeprom(struct bttv *b { if( strncmp(&(eeprom_data[0x1e]),"Temic 4066 FY5",14) ==0) { btv->tuner_type=TUNER_TEMIC_4066FY5_PAL_I; - printk("bttv Modtec: Tuner autodetected %s\n", - &eeprom_data[0x1e]); + printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n", + btv->c.nr,&eeprom_data[0x1e]); + } else if (strncmp(&(eeprom_data[0x1e]),"Alps TSBB5",10) ==0) { + btv->tuner_type=TUNER_ALPS_TSBB5_PAL_I; + printk("bttv%d: Modtec: Tuner autodetected by eeprom: %s\n", + btv->c.nr,&eeprom_data[0x1e]); } else { - printk("bttv Modtec: Unknown TunerString:%s\n", - &eeprom_data[0x1e]); + printk("bttv%d: Modtec: Unknown TunerString: %s\n", + btv->c.nr,&eeprom_data[0x1e]); } } @@ -2410,7 +2680,7 @@ static void __devinit hauppauge_eeprom(s if (eeprom_data[0] != 0x84 || eeprom_data[2] != 0) printk(KERN_WARNING "bttv%d: Hauppauge eeprom: invalid\n", - btv->nr); + btv->c.nr); /* Block 2 starts after len+3 bytes header */ blk2 = eeprom_data[1] + 3; @@ -2428,7 +2698,7 @@ static void __devinit hauppauge_eeprom(s if (bttv_verbose) printk(KERN_INFO "bttv%d: Hauppauge eeprom: model=%d, " "tuner=%s (%d), radio=%s\n", - btv->nr, model, hauppauge_tuner[tuner].name, + btv->c.nr, model, hauppauge_tuner[tuner].name, btv->tuner_type, radio ? "yes" : "no"); } @@ -2452,7 +2722,7 @@ static int terratec_active_radio_upgrade tea5757_write(btv, 5 * freq + 0x358); // write 0x1ed8 if (0x1ed8 == tea5757_read(btv)) { printk("bttv%d: Terratec Active Radio Upgrade found.\n", - btv->nr); + btv->c.nr); btv->has_radio = 1; btv->has_matchbox = 1; } else { @@ -2483,36 +2753,35 @@ static int __devinit pvr_altera_load(str u32 n; u8 bits; int i; - - btwrite(BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG, - BT848_GPIO_OUT_EN); - btwrite(0,BT848_GPIO_DATA); + + gpio_inout(0xffffff,BTTV_ALT_DATA|BTTV_ALT_DCLK|BTTV_ALT_NCONFIG); + gpio_write(0); udelay(PVR_GPIO_DELAY); - btwrite(BTTV_ALT_NCONFIG,BT848_GPIO_DATA); + gpio_write(BTTV_ALT_NCONFIG); udelay(PVR_GPIO_DELAY); for (n = 0; n < microlen; n++) { bits = micro[n]; for ( i = 0 ; i < 8 ; i++ ) { - btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); - if (bits & 0x01) - btor(BTTV_ALT_DATA,BT848_GPIO_DATA); + gpio_bits(BTTV_ALT_DCLK,0); + if (bits & 0x01) + gpio_bits(BTTV_ALT_DATA,BTTV_ALT_DATA); else - btand(~BTTV_ALT_DATA,BT848_GPIO_DATA); - btor(BTTV_ALT_DCLK,BT848_GPIO_DATA); + gpio_bits(BTTV_ALT_DATA,0); + gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK); bits >>= 1; } } - btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); + gpio_bits(BTTV_ALT_DCLK,0); udelay(PVR_GPIO_DELAY); /* begin Altera init loop (Not necessary,but doesn't hurt) */ for (i = 0 ; i < 30 ; i++) { - btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); - btor(BTTV_ALT_DCLK,BT848_GPIO_DATA); + gpio_bits(BTTV_ALT_DCLK,0); + gpio_bits(BTTV_ALT_DCLK,BTTV_ALT_DCLK); } - btand(~BTTV_ALT_DCLK,BT848_GPIO_DATA); + gpio_bits(BTTV_ALT_DCLK,0); return 0; } @@ -2535,15 +2804,15 @@ int __devinit pvr_boot(struct bttv *btv) microlen = mod_firmware_load(firm_altera, (char**) µ); if (!microlen) { printk(KERN_WARNING "bttv%d: altera firmware not found [%s]\n", - btv->nr, firm_altera); + btv->c.nr, firm_altera); return -1; } printk(KERN_INFO "bttv%d: uploading altera firmware [%s] ...\n", - btv->nr, firm_altera); + btv->c.nr, firm_altera); result = pvr_altera_load(btv, micro, microlen); printk(KERN_INFO "bttv%d: ... upload %s\n", - btv->nr, (result < 0) ? "failed" : "ok"); + btv->c.nr, (result < 0) ? "failed" : "ok"); vfree(micro); return result; } @@ -2555,15 +2824,15 @@ int __devinit pvr_boot(struct bttv *btv) const struct firmware *fw_entry; int rc; - rc = request_firmware(&fw_entry, "hcwamc.rbf", pci_name(btv->dev)); + rc = request_firmware(&fw_entry, "hcwamc.rbf", pci_name(btv->c.pci)); if (rc != 0) { printk(KERN_WARNING "bttv%d: no altera firmware [via hotplug]\n", - btv->nr); + btv->c.nr); return rc; } rc = pvr_altera_load(btv, fw_entry->data, fw_entry->size); printk(KERN_INFO "bttv%d: altera firmware upload %s\n", - btv->nr, (rc < 0) ? "failed" : "ok"); + btv->c.nr, (rc < 0) ? "failed" : "ok"); release_firmware(fw_entry); return rc; } @@ -2578,7 +2847,7 @@ static void __devinit osprey_eeprom(stru unsigned char *ee = eeprom_data; unsigned long serial = 0; - if (btv->type == 0) { + if (btv->c.type == 0) { /* this might be an antique... check for MMAC label in eeprom */ if ((ee[0]=='M') && (ee[1]=='M') && (ee[2]=='A') && (ee[3]=='C')) { unsigned char checksum = 0; @@ -2586,7 +2855,7 @@ static void __devinit osprey_eeprom(stru checksum += ee[i]; if (checksum != ee[21]) return; - btv->type = BTTV_OSPREY1x0_848; + btv->c.type = BTTV_OSPREY1x0_848; for (i = 12; i < 21; i++) serial *= 10, serial += ee[i] - '0'; } @@ -2615,49 +2884,49 @@ static void __devinit osprey_eeprom(stru /* 848 based */ case 0x0004: - btv->type = BTTV_OSPREY1x0_848; + btv->c.type = BTTV_OSPREY1x0_848; break; case 0x0005: - btv->type = BTTV_OSPREY101_848; + btv->c.type = BTTV_OSPREY101_848; break; /* 878 based */ case 0x0012: case 0x0013: - btv->type = BTTV_OSPREY1x0; + btv->c.type = BTTV_OSPREY1x0; break; case 0x0014: case 0x0015: - btv->type = BTTV_OSPREY1x1; + btv->c.type = BTTV_OSPREY1x1; break; case 0x0016: case 0x0017: case 0x0020: - btv->type = BTTV_OSPREY1x1_SVID; + btv->c.type = BTTV_OSPREY1x1_SVID; break; case 0x0018: case 0x0019: case 0x001E: case 0x001F: - btv->type = BTTV_OSPREY2xx; + btv->c.type = BTTV_OSPREY2xx; break; case 0x001A: case 0x001B: - btv->type = BTTV_OSPREY2x0_SVID; + btv->c.type = BTTV_OSPREY2x0_SVID; break; case 0x0040: - btv->type = BTTV_OSPREY500; + btv->c.type = BTTV_OSPREY500; break; case 0x0050: case 0x0056: - btv->type = BTTV_OSPREY540; + btv->c.type = BTTV_OSPREY540; /* bttv_osprey_540_init(btv); */ break; case 0x0060: case 0x0070: - btv->type = BTTV_OSPREY2x0; + btv->c.type = BTTV_OSPREY2x0; //enable output on select control lines - btwrite(0x000303, BT848_GPIO_OUT_EN); + gpio_inout(0xffffff,0x000303); break; default: /* unknown...leave generic, but get serial # */ @@ -2670,7 +2939,7 @@ static void __devinit osprey_eeprom(stru } printk(KERN_INFO "bttv%d: osprey eeprom: card=%d name=%s serial=%ld\n", - btv->nr, btv->type, bttv_tvcards[btv->type].name,serial); + btv->c.nr, btv->c.type, bttv_tvcards[btv->c.type].name,serial); } /* ----------------------------------------------------------------------- */ @@ -2715,7 +2984,7 @@ static void __devinit avermedia_eeprom(s tuner = tuner_1_table[tuner_format]; printk(KERN_INFO "bttv%d: Avermedia eeprom[0x%02x%02x]: tuner=", - btv->nr,eeprom_data[0x41],eeprom_data[0x42]); + btv->c.nr,eeprom_data[0x41],eeprom_data[0x42]); if(tuner) { btv->tuner_type=tuner; printk("%d",tuner); @@ -2741,8 +3010,8 @@ void bttv_tda9880_setnorm(struct bttv *b dprintk("bttv_tda9880_setnorm to PAL\n"); } // set GPIO according - btaor(bttv_tvcards[btv->type].audiomux[btv->audio], - ~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); + gpio_bits(bttv_tvcards[btv->c.type].gpiomask, + bttv_tvcards[btv->c.type].audiomux[btv->audio]); } @@ -2757,23 +3026,23 @@ static void __devinit boot_msp34xx(struc { int mask = (1 << pin); - btaor(mask, ~mask, BT848_GPIO_OUT_EN); - btaor(0, ~mask, BT848_GPIO_DATA); + gpio_inout(mask,mask); + gpio_bits(mask,0); udelay(2500); - btaor(mask, ~mask, BT848_GPIO_DATA); + gpio_bits(mask,mask); + if (bttv_gpio) bttv_gpio_tracking(btv,"msp34xx"); - if (bttv_verbose) printk(KERN_INFO "bttv%d: Hauppauge/Voodoo msp34xx: reset line " - "init [%d]\n", btv->nr, pin); + "init [%d]\n", btv->c.nr, pin); } static void __devinit boot_bt832(struct bttv *btv) { - int outbits,databits,resetbit=0; + int resetbit=0; - switch (btv->type) { + switch (btv->c.type) { case BTTV_PXELVWPLTVPAK: resetbit = 0x400000; break; @@ -2787,18 +3056,14 @@ static void __devinit boot_bt832(struct request_module("bt832"); bttv_call_i2c_clients(btv, BT832_HEXDUMP, NULL); - printk("bttv%d: Reset Bt832 [line=0x%x]\n",btv->nr,resetbit); - btwrite(0, BT848_GPIO_DATA); - outbits = btread(BT848_GPIO_OUT_EN); - databits= btread(BT848_GPIO_DATA); - btwrite(resetbit, BT848_GPIO_OUT_EN); + printk("bttv%d: Reset Bt832 [line=0x%x]\n",btv->c.nr,resetbit); + gpio_write(0); + gpio_inout(resetbit, resetbit); udelay(5); - btwrite(resetbit, BT848_GPIO_DATA); + gpio_bits(resetbit, resetbit); udelay(5); - btwrite(0, BT848_GPIO_DATA); + gpio_bits(resetbit, 0); udelay(5); - btwrite(outbits, BT848_GPIO_OUT_EN); - btwrite(databits, BT848_GPIO_DATA); // bt832 on pixelview changes from i2c 0x8a to 0x88 after // being reset as above. So we must follow by this: @@ -2821,13 +3086,13 @@ static void __devinit init_PXC200(struct u32 val; /* Initialise GPIO-connevted stuff */ - btwrite(1<<13,BT848_GPIO_OUT_EN); /* Reset pin only */ - btwrite(0,BT848_GPIO_DATA); + gpio_inout(0xffffff, (1<<13)); + gpio_write(0); udelay(3); - btwrite(1<<13,BT848_GPIO_DATA); + gpio_write(1<<13); /* GPIO inputs are pulled up, so no need to drive * reset pin any longer */ - btwrite(0,BT848_GPIO_OUT_EN); + gpio_bits(0xffffff, 0); if (bttv_gpio) bttv_gpio_tracking(btv,"pxc200"); @@ -2862,10 +3127,10 @@ static void __devinit init_PXC200(struct * device same as above for the reset line, but not the same * value sent to the GPIO-connected stuff * which one is the good one? */ - btwrite( (1<<2), BT848_GPIO_OUT_EN); /* only the reset pin */ - btwrite(0, BT848_GPIO_DATA); + gpio_inout(0xffffff,(1<<2)); + gpio_write(0); udelay(10); - btwrite(1<<2, BT848_GPIO_DATA); + gpio_write(1<<2); for (i = 0; i < ARRAY_SIZE(vals); i++) { tmp=bttv_I2CWrite(btv,0x1E,0,vals[i],1); @@ -2892,17 +3157,16 @@ static void __devinit init_PXC200(struct void bus_low(struct bttv *btv, int bit) { if (btv->mbox_ior) { - btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - BT848_GPIO_DATA); + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); udelay(5); } - btand(~(bit), BT848_GPIO_DATA); + gpio_bits(bit,0); udelay(5); if (btv->mbox_ior) { - btand(~(btv->mbox_iow | btv->mbox_csel), - BT848_GPIO_DATA); + gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); udelay(5); } } @@ -2910,17 +3174,16 @@ void bus_low(struct bttv *btv, int bit) void bus_high(struct bttv *btv, int bit) { if (btv->mbox_ior) { - btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - BT848_GPIO_DATA); + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); udelay(5); } - btor((bit), BT848_GPIO_DATA); + gpio_bits(bit,bit); udelay(5); if (btv->mbox_ior) { - btand(~(btv->mbox_iow | btv->mbox_csel), - BT848_GPIO_DATA); + gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); udelay(5); } } @@ -2928,15 +3191,14 @@ void bus_high(struct bttv *btv, int bit) int bus_in(struct bttv *btv, int bit) { if (btv->mbox_ior) { - btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - BT848_GPIO_DATA); + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); udelay(5); - btand(~(btv->mbox_ior | btv->mbox_csel), - BT848_GPIO_DATA); + gpio_bits(btv->mbox_iow | btv->mbox_csel, 0); udelay(5); } - return btread(BT848_GPIO_DATA) & (bit); + return gpio_read() & (bit); } /* TEA5757 register bits */ @@ -2974,12 +3236,11 @@ static int tea5757_read(struct bttv *btv int i; /* better safe than sorry */ - btaor((btv->mbox_clk | btv->mbox_we), - ~btv->mbox_mask, BT848_GPIO_OUT_EN); + gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we); if (btv->mbox_ior) { - btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - BT848_GPIO_DATA); + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); udelay(5); } @@ -2996,11 +3257,11 @@ static int tea5757_read(struct bttv *btv while (bus_in(btv,btv->mbox_data) && time_before(jiffies, timeout)) schedule(); if (bus_in(btv,btv->mbox_data)) { - printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->nr); + printk(KERN_WARNING "bttv%d: tea5757: read timeout\n",btv->c.nr); return -1; } - dprintk("bttv%d: tea5757:",btv->nr); + dprintk("bttv%d: tea5757:",btv->c.nr); for(i = 0; i < 24; i++) { udelay(5); @@ -3012,7 +3273,7 @@ static int tea5757_read(struct bttv *btv value |= (bus_in(btv,btv->mbox_data) == 0)?0:1; /* MSB first */ dprintk("%c", (bus_in(btv,btv->mbox_most) == 0)?'S':'M'); } - dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->nr, value); + dprintk("\nbttv%d: tea5757: read 0x%X\n", btv->c.nr, value); return value; } @@ -3021,18 +3282,17 @@ static int tea5757_write(struct bttv *bt int i; int reg = value; - btaor(btv->mbox_clk | btv->mbox_we | btv->mbox_data, - ~btv->mbox_mask, BT848_GPIO_OUT_EN); + gpio_inout(btv->mbox_mask, btv->mbox_clk | btv->mbox_we | btv->mbox_data); if (btv->mbox_ior) { - btor(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, - BT848_GPIO_DATA); + gpio_bits(btv->mbox_ior | btv->mbox_iow | btv->mbox_csel, + btv->mbox_ior | btv->mbox_iow | btv->mbox_csel); udelay(5); } if (bttv_gpio) bttv_gpio_tracking(btv,"tea5757 write"); - dprintk("bttv%d: tea5757: write 0x%X\n", btv->nr, value); + dprintk("bttv%d: tea5757: write 0x%X\n", btv->c.nr, value); bus_low(btv,btv->mbox_clk); bus_high(btv,btv->mbox_we); for(i = 0; i < 25; i++) @@ -3058,7 +3318,7 @@ void tea5757_set_freq(struct bttv *btv, #if 0 /* breaks Miro PCTV */ value = tea5757_read(btv); - dprintk("bttv%d: tea5757 readback=0x%x\n",btv->nr,value); + dprintk("bttv%d: tea5757 readback=0x%x\n",btv->c.nr,value); #endif } @@ -3084,7 +3344,7 @@ void winview_audio(struct bttv *btv, str /* tens */ bits_out |= (PT2254_DBS_IN_10>>(vol/5)); bits_out |= PT2254_L_CHANNEL | PT2254_R_CHANNEL; - data = btread(BT848_GPIO_DATA); + data = gpio_read(); data &= ~(WINVIEW_PT2254_CLK| WINVIEW_PT2254_DATA| WINVIEW_PT2254_STROBE); for (loops = 17; loops >= 0 ; loops--) { @@ -3092,20 +3352,20 @@ void winview_audio(struct bttv *btv, str data |= WINVIEW_PT2254_DATA; else data &= ~WINVIEW_PT2254_DATA; - btwrite(data, BT848_GPIO_DATA); + gpio_write(data); udelay(5); data |= WINVIEW_PT2254_CLK; - btwrite(data, BT848_GPIO_DATA); + gpio_write(data); udelay(5); data &= ~WINVIEW_PT2254_CLK; - btwrite(data, BT848_GPIO_DATA); + gpio_write(data); } data |= WINVIEW_PT2254_STROBE; data &= ~WINVIEW_PT2254_DATA; - btwrite(data, BT848_GPIO_DATA); + gpio_write(data); udelay(10); data &= ~WINVIEW_PT2254_STROBE; - btwrite(data, BT848_GPIO_DATA); + gpio_write(data); } /* ----------------------------------------------------------------------- */ @@ -3118,7 +3378,7 @@ gvbctv3pci_audio(struct bttv *btv, struc unsigned int con = 0; if (set) { - btor(0x300, BT848_GPIO_OUT_EN); + gpio_inout(0x300, 0x300); if (v->mode & VIDEO_SOUND_LANG1) con = 0x000; if (v->mode & VIDEO_SOUND_LANG2) @@ -3127,13 +3387,68 @@ gvbctv3pci_audio(struct bttv *btv, struc con = 0x200; // if (v->mode & VIDEO_SOUND_MONO) // con = 0x100; - btaor(con, ~0x300, BT848_GPIO_DATA); + gpio_bits(0x300, con); } else { v->mode = VIDEO_SOUND_STEREO | VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; } } +static void +gvbctv5pci_audio(struct bttv *btv, struct video_audio *v, int set) +{ + unsigned int val, con; + +#if BTTV_VERSION_CODE > KERNEL_VERSION(0,8,0) + if (btv->radio_user) + return; +#else + if (btv->radio) + return; +#endif + + val = gpio_read(); + if (set) { + con = 0x000; + if (v->mode & VIDEO_SOUND_LANG2) { + if (v->mode & VIDEO_SOUND_LANG1) { + /* LANG1 + LANG2 */ + con = 0x100; + } + else { + /* LANG2 */ + con = 0x300; + } + } + if (con != (val & 0x300)) { + gpio_bits(0x300, con); + if (bttv_gpio) + bttv_gpio_tracking(btv,"gvbctv5pci"); + } + } else { + switch (val & 0x70) { + case 0x10: + v->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + break; + case 0x30: + v->mode = VIDEO_SOUND_LANG2; + break; + case 0x50: + v->mode = VIDEO_SOUND_LANG1; + break; + case 0x60: + v->mode = VIDEO_SOUND_STEREO; + break; + case 0x70: + v->mode = VIDEO_SOUND_MONO; + break; + default: + v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO | + VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + } + } +} + /* * Mario Medina Nussbaum <medisoft@alohabbs.org.mx> * I discover that on BT848_GPIO_DATA address a byte 0xcce enable stereo, @@ -3153,12 +3468,12 @@ avermedia_tvphone_audio(struct bttv *btv int val = 0; if (set) { - if (v->mode & VIDEO_SOUND_LANG1) /* SAP */ + if (v->mode & VIDEO_SOUND_LANG2) /* SAP */ val = 0x02; if (v->mode & VIDEO_SOUND_STEREO) val = 0x01; if (val) { - btaor(val, ~0x03, BT848_GPIO_DATA); + gpio_bits(0x03,val); if (bttv_gpio) bttv_gpio_tracking(btv,"avermedia"); } @@ -3169,13 +3484,33 @@ avermedia_tvphone_audio(struct bttv *btv } } +static void +avermedia_tv_stereo_audio(struct bttv *btv, struct video_audio *v, int set) +{ + int val = 0; + + if (set) { + if (v->mode & VIDEO_SOUND_LANG2) /* SAP */ + val = 0x01; + if (v->mode & VIDEO_SOUND_STEREO) /* STEREO */ + val = 0x02; + btaor(val, ~0x03, BT848_GPIO_DATA); + if (bttv_gpio) + bttv_gpio_tracking(btv,"avermedia"); + } else { + v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO | + VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; + return; + } +} + /* Lifetec 9415 handling */ static void lt9415_audio(struct bttv *btv, struct video_audio *v, int set) { int val = 0; - if (btread(BT848_GPIO_DATA) & 0x4000) { + if (gpio_read() & 0x4000) { v->mode = VIDEO_SOUND_MONO; return; } @@ -3188,7 +3523,7 @@ lt9415_audio(struct bttv *btv, struct vi if ((v->mode & VIDEO_SOUND_LANG1) || (v->mode & VIDEO_SOUND_MONO)) val = 0; - btaor(val, ~0x0880, BT848_GPIO_DATA); + gpio_bits(0x0880, val); if (bttv_gpio) bttv_gpio_tracking(btv,"lt9415"); } else { @@ -3206,12 +3541,12 @@ terratv_audio(struct bttv *btv, struct v unsigned int con = 0; if (set) { - btor(0x180000, BT848_GPIO_OUT_EN); + gpio_inout(0x180000,0x180000); if (v->mode & VIDEO_SOUND_LANG2) con = 0x080000; if (v->mode & VIDEO_SOUND_STEREO) con = 0x180000; - btaor(con, ~0x180000, BT848_GPIO_DATA); + gpio_bits(0x180000, con); if (bttv_gpio) bttv_gpio_tracking(btv,"terratv"); } else { @@ -3236,7 +3571,7 @@ winfast2000_audio(struct bttv *btv, stru if (v->mode & VIDEO_SOUND_STEREO) /* Stereo */ val = 0x020000; if (val) { - btaor(val, ~0x430000, BT848_GPIO_DATA); + gpio_bits(0x430000, val); if (bttv_gpio) bttv_gpio_tracking(btv,"winfast2000"); } @@ -3276,7 +3611,7 @@ pvbt878p9b_audio(struct bttv *btv, struc val = 0x02; } if (val) { - btaor(val, ~0x03, BT848_GPIO_DATA); + gpio_bits(0x03,val); if (bttv_gpio) bttv_gpio_tracking(btv,"pvbt878p9b"); } @@ -3312,7 +3647,7 @@ fv2000s_audio(struct bttv *btv, struct v val = 0x1080; //-dk-???: 0x0880, 0x0080, 0x1800 ... } if (val != 0xffff) { - btaor(val, ~0x1800, BT848_GPIO_DATA); + gpio_bits(0x1800, val); if (bttv_gpio) bttv_gpio_tracking(btv,"fv2000s"); } @@ -3341,7 +3676,7 @@ windvr_audio(struct bttv *btv, struct vi if (v->mode & VIDEO_SOUND_STEREO) val = 0; if (val) { - btaor(val, ~0x140000, BT848_GPIO_DATA); + gpio_bits(0x140000, val); if (bttv_gpio) bttv_gpio_tracking(btv,"windvr"); } @@ -3373,7 +3708,7 @@ adtvk503_audio(struct bttv *btv, struct if (v->mode & VIDEO_SOUND_MONO) con = 0x00060000; if (con != 0xffffff) { - btaor(con, ~0x1e0000, BT848_GPIO_DATA); + gpio_bits(0x1e0000,con); if (bttv_gpio) bttv_gpio_tracking(btv, "adtvk503"); } @@ -3410,16 +3745,16 @@ adtvk503_audio(struct bttv *btv, struct static void rv605_muxsel(struct bttv *btv, unsigned int input) { /* reset all conections */ - btaor(0x200,~0x200, BT848_GPIO_DATA); + gpio_bits(0x200,0x200); mdelay(1); - btaor(0x000,~0x200, BT848_GPIO_DATA); + gpio_bits(0x200,0x000); mdelay(1); /* create a new conection */ - btaor(0x080,~0x480, BT848_GPIO_DATA); - btaor(0x480,~0x480, BT848_GPIO_DATA); + gpio_bits(0x480,0x080); + gpio_bits(0x480,0x480); mdelay(1); - btaor(0x080,~0x480, BT848_GPIO_DATA); + gpio_bits(0x480,0x080); mdelay(1); } @@ -3449,9 +3784,75 @@ static void xguard_muxsel(struct bttv *b ENB1, ENB1|IN01, ENB1|IN11, ENB1|IN01|IN11, ENA1, ENA1|IN01, ENA1|IN11, ENA1|IN01|IN11, }; - btwrite(masks[input%16], BT848_GPIO_DATA); + gpio_write(masks[input%16]); +} + +/* + * ivc120_muxsel [Added by Alan Garfield <alan@fromorbit.com>] + * + * The IVC120G security card has 4 i2c controlled TDA8540 matrix + * swichers to provide 16 channels to MUX0. The TDA8540's have + * 4 indepedant outputs and as such the IVC120G also has the + * optional "Monitor Out" bus. This allows the card to be looking + * at one input while the monitor is looking at another. + * + * Since I've couldn't be bothered figuring out how to add an + * independant muxsel for the monitor bus, I've just set it to + * whatever the card is looking at. + * + * OUT0 of the TDA8540's is connected to MUX0 (0x03) + * OUT1 of the TDA8540's is connected to "Monitor Out" (0x0C) + * + * TDA8540_ALT3 IN0-3 = Channel 13 - 16 (0x03) + * TDA8540_ALT4 IN0-3 = Channel 1 - 4 (0x03) + * TDA8540_ALT5 IN0-3 = Channel 5 - 8 (0x03) + * TDA8540_ALT6 IN0-3 = Channel 9 - 12 (0x03) + * + */ + +/* All 7 possible sub-ids for the TDA8540 Matrix Switcher */ +#define I2C_TDA8540 0x90 +#define I2C_TDA8540_ALT1 0x92 +#define I2C_TDA8540_ALT2 0x94 +#define I2C_TDA8540_ALT3 0x96 +#define I2C_TDA8540_ALT4 0x98 +#define I2C_TDA8540_ALT5 0x9a +#define I2C_TDA8540_ALT6 0x9c + +static void ivc120_muxsel(struct bttv *btv, unsigned int input) +{ + // Simple maths + int key = input % 4; + int matrix = input / 4; + + dprintk("bttv%d: ivc120_muxsel: Input - %02d | TDA - %02d | In - %02d\n", + btv->c.nr, input, matrix, key); + + // Handles the input selection on the TDA8540's + bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x00, + ((matrix == 3) ? (key | key << 2) : 0x00), 1); + bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x00, + ((matrix == 0) ? (key | key << 2) : 0x00), 1); + bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x00, + ((matrix == 1) ? (key | key << 2) : 0x00), 1); + bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x00, + ((matrix == 2) ? (key | key << 2) : 0x00), 1); + + // Handles the output enables on the TDA8540's + bttv_I2CWrite(btv, I2C_TDA8540_ALT3, 0x02, + ((matrix == 3) ? 0x03 : 0x00), 1); // 13 - 16 + bttv_I2CWrite(btv, I2C_TDA8540_ALT4, 0x02, + ((matrix == 0) ? 0x03 : 0x00), 1); // 1-4 + bttv_I2CWrite(btv, I2C_TDA8540_ALT5, 0x02, + ((matrix == 1) ? 0x03 : 0x00), 1); // 5-8 + bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02, + ((matrix == 2) ? 0x03 : 0x00), 1); // 9-12 + + // Selects MUX0 for input on the 878 + btaor((0)<<5, ~(3<<5), BT848_IFORM); } + /* ----------------------------------------------------------------------- */ /* motherboard chipset specific stuff */ @@ -3511,12 +3912,12 @@ int __devinit bttv_handle_chipset(struct if (bttv_verbose) { if (triton1) - printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->nr); + printk(KERN_INFO "bttv%d: enabling ETBF (430FX/VP3 compatibilty)\n",btv->c.nr); if (vsfx && btv->id >= 878) - printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->nr); + printk(KERN_INFO "bttv%d: enabling VSFX\n",btv->c.nr); if (UNSET != latency) printk(KERN_INFO "bttv%d: setting pci timer to %d\n", - btv->nr,latency); + btv->c.nr,latency); } if (btv->id < 878) { @@ -3525,19 +3926,99 @@ int __devinit bttv_handle_chipset(struct btv->triton1 = BT848_INT_ETBF; } else { /* bt878 has a bit in the pci config space for it */ - pci_read_config_byte(btv->dev, BT878_DEVCTRL, &command); + pci_read_config_byte(btv->c.pci, BT878_DEVCTRL, &command); if (triton1) command |= BT878_EN_TBFX; if (vsfx) command |= BT878_EN_VSFX; - pci_write_config_byte(btv->dev, BT878_DEVCTRL, command); + pci_write_config_byte(btv->c.pci, BT878_DEVCTRL, command); } if (UNSET != latency) - pci_write_config_byte(btv->dev, PCI_LATENCY_TIMER, latency); + pci_write_config_byte(btv->c.pci, PCI_LATENCY_TIMER, latency); return 0; } +/* PXC200 muxsel helper + * luke@syseng.anu.edu.au + * another transplant + * from Alessandro Rubini (rubini@linux.it) + * + * There are 4 kinds of cards: + * PXC200L which is bt848 + * PXC200F which is bt848 with PIC controlling mux + * PXC200AL which is bt878 + * PXC200AF which is bt878 with PIC controlling mux + */ +#define PX_CFG_PXC200F 0x01 +#define PX_FLAG_PXC200A 0x00001000 /* a pxc200A is bt-878 based */ +#define PX_I2C_PIC 0x0f +#define PX_PXC200A_CARDID 0x200a1295 +#define PX_I2C_CMD_CFG 0x00 + +static void PXC200_muxsel(struct bttv *btv, unsigned int input) +{ + int rc; + long mux; + int bitmask; + unsigned char buf[2]; + + /* Read PIC config to determine if this is a PXC200F */ + /* PX_I2C_CMD_CFG*/ + buf[0]=0; + buf[1]=0; + rc=bttv_I2CWrite(btv,(PX_I2C_PIC<<1),buf[0],buf[1],1); + if (rc) { + printk(KERN_DEBUG "bttv%d: PXC200_muxsel: pic cfg write failed:%d\n", btv->c.nr,rc); + /* not PXC ? do nothing */ + return; + } + + rc=bttv_I2CRead(btv,(PX_I2C_PIC<<1),0); + if (!(rc & PX_CFG_PXC200F)) { + printk(KERN_DEBUG "bttv%d: PXC200_muxsel: not PXC200F rc:%d \n", btv->c.nr,rc); + return; + } + + + /* The multiplexer in the 200F is handled by the GPIO port */ + /* get correct mapping between inputs */ + /* mux = bttv_tvcards[btv->type].muxsel[input] & 3; */ + /* ** not needed!? */ + mux = input; + + /* make sure output pins are enabled */ + /* bitmask=0x30f; */ + bitmask=0x302; + /* check whether we have a PXC200A */ + if (btv->cardid == PX_PXC200A_CARDID) { + bitmask ^= 0x180; /* use 7 and 9, not 8 and 9 */ + bitmask |= 7<<4; /* the DAC */ + } + btwrite(bitmask, BT848_GPIO_OUT_EN); + + bitmask = btread(BT848_GPIO_DATA); + if (btv->cardid == PX_PXC200A_CARDID) + bitmask = (bitmask & ~0x280) | ((mux & 2) << 8) | ((mux & 1) << 7); + else /* older device */ + bitmask = (bitmask & ~0x300) | ((mux & 3) << 8); + btwrite(bitmask,BT848_GPIO_DATA); + + /* + * Was "to be safe, set the bt848 to input 0" + * Actually, since it's ok at load time, better not messing + * with these bits (on PXC200AF you need to set mux 2 here) + * + * needed because bttv-driver sets mux before calling this function + */ + if (btv->cardid == PX_PXC200A_CARDID) + btaor(2<<5, ~BT848_IFORM_MUXSEL, BT848_IFORM); + else /* older device */ + btand(~BT848_IFORM_MUXSEL,BT848_IFORM); + + printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux); +} + /* * Local variables: * c-basic-offset: 8 diff -up linux-2.4.26/drivers/media/video/bttv-driver.c linux/drivers/media/video/bttv-driver.c --- linux-2.4.26/drivers/media/video/bttv-driver.c 2004-04-21 13:45:25.000000000 +0200 +++ linux/drivers/media/video/bttv-driver.c 2004-04-21 14:11:41.000000000 +0200 @@ -1,97 +1,88 @@ /* 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-2003 Gerd Knorr <kraxel@goldbach.in-berlin.de> - + + Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de> + & Marcus Metzler <mocm@thp.uni-koeln.de> + (c) 1999-2002 Gerd Knorr <kraxel@bytesex.org> + + some v4l2 code lines are taken from Justin's bttv2 driver which is + (c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za> + 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/init.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 <linux/ioport.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 <linux/pagemap.h> +#include <linux/kdev_t.h> #include <asm/io.h> -#include <asm/pgtable.h> -#include <asm/page.h> #include <asm/byteorder.h> #include "bttvp.h" -#define DEBUG(x) /* Debug driver */ -#define MIN(a,b) (((a)>(b))?(b):(a)) -#define MAX(a,b) (((a)>(b))?(a):(b)) - -/* fwd decl */ -static void bt848_set_risc_jmps(struct bttv *btv, int state); -static void make_vbitab(struct bttv *btv); -static void bt848_set_winsize(struct bttv *btv); - unsigned int bttv_num; /* number of Bt848s in use */ struct bttv bttvs[BTTV_MAX]; -/* configuration variables */ +unsigned int bttv_debug = 0; +unsigned int bttv_verbose = 1; +unsigned int bttv_gpio = 0; + +/* config variables */ #ifdef __BIG_ENDIAN 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 gpint = 1; static unsigned int irq_debug = 0; -static unsigned int gbuffers = 4; -static unsigned int gbufsize = BTTV_MAX_FBUF; +static unsigned int gbuffers = 8; +static unsigned int gbufsize = 0x208000; -static unsigned int combfilter = 0; -static unsigned int lumafilter = 0; -static unsigned int automute = 1; -static unsigned int chroma_agc = 0; -static unsigned int adc_crush = 1; 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; +static int debug_latency = 0; + +static unsigned int fdsr = 0; -/* insmod options */ +/* options */ +static unsigned int combfilter = 0; +static unsigned int lumafilter = 0; +static unsigned int automute = 1; +static unsigned int chroma_agc = 0; +static unsigned int adc_crush = 1; +static unsigned int whitecrush_upper = 0xCF; +static unsigned int whitecrush_lower = 0x7F; +static unsigned int vcr_hack = 0; +static unsigned int irq_iswitch = 0; + +/* API features (turn on/off stuff for testing) */ +static unsigned int v4l2 = 1; + + +/* insmod args */ MODULE_PARM(radio,"1-" __stringify(BTTV_MAX) "i"); 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"); @@ -101,10 +92,16 @@ 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_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); MODULE_PARM(gbufsize,"i"); MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); -MODULE_PARM(gpint,"i"); + +MODULE_PARM(video_nr,"i"); +MODULE_PARM(radio_nr,"i"); +MODULE_PARM(vbi_nr,"i"); +MODULE_PARM(debug_latency,"i"); + +MODULE_PARM(fdsr,"i"); MODULE_PARM(combfilter,"i"); MODULE_PARM(lumafilter,"i"); @@ -114,12 +111,18 @@ MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); MODULE_PARM(adc_crush,"i"); MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); +MODULE_PARM(whitecrush_upper,"i"); +MODULE_PARM_DESC(whitecrush_upper,"sets the white crush upper value, default is 207"); +MODULE_PARM(whitecrush_lower,"i"); +MODULE_PARM_DESC(whitecrush_lower,"sets the white crush lower value, default is 127"); +MODULE_PARM(vcr_hack,"i"); +MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); +MODULE_PARM(irq_iswitch,"i"); +MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); -MODULE_PARM(video_nr,"i"); -MODULE_PARM(radio_nr,"i"); -MODULE_PARM(vbi_nr,"i"); +MODULE_PARM(v4l2,"i"); -MODULE_DESCRIPTION("bttv - v4l driver module for bt848/878 based cards"); +MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards"); MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); MODULE_LICENSE("GPL"); @@ -129,303 +132,12 @@ __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 */ -/*******************************/ - - -static inline unsigned long kvirt_to_bus(unsigned long adr) -{ - unsigned long kva; - - kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - return virt_to_bus((void *)kva); -} - -/* 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 kva; - - kva = (unsigned long)page_address(vmalloc_to_page((void *)adr)); - kva |= adr & (PAGE_SIZE-1); /* restore the offset */ - return __pa(kva); -} - -static void * rvmalloc(signed long size) -{ - struct page *page; - void * mem; - unsigned long adr; - - 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 = vmalloc_to_page((void *)adr); - mem_map_reserve(page); - adr+=PAGE_SIZE; - size-=PAGE_SIZE; - } - } - return mem; -} - -static void rvfree(void * mem, signed long size) -{ - struct page *page; - unsigned long adr; - - if (mem) { - adr=(unsigned long) mem; - while (size > 0) { - page = vmalloc_to_page((void *)adr); - mem_map_unreserve(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; -} - /* ----------------------------------------------------------------------- */ +/* sysfs */ -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) -{ - if (bttv_tvcards[btv->type].gpiomask) - 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; - if (bttv_tvcards[btv->type].gpiomask) - 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 (!in_interrupt()) - bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mode)); -} - - -static inline void bt848_dma(struct bttv *btv, uint state) -{ - if (state) - btor(3, BT848_GPIO_DMA_CTL); - else - btand(~3, BT848_GPIO_DMA_CTL); -} - - -/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/ - -/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C - PLL_X = Reference pre-divider (0=1, 1=2) - PLL_C = Post divider (0=6, 1=4) - PLL_I = Integer input - PLL_F = Fractional input - - F_input = 28.636363 MHz: - PAL (CLKx2 = 35.46895 MHz): PLL_X = 1, PLL_I = 0x0E, PLL_F = 0xDCF9, PLL_C = 0 -*/ - -static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) -{ - unsigned char fl, fh, fi; - - /* prevent overflows */ - fin/=4; - fout/=4; - - fout*=12; - fi=fout/fin; - - fout=(fout%fin)*256; - fh=fout/fin; - - fout=(fout%fin)*256; - fl=fout/fin; - - /*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/ - btwrite(fl, BT848_PLL_F_LO); - btwrite(fh, BT848_PLL_F_HI); - btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); -} - -static void set_pll(struct bttv *btv) -{ - int i; - - if (!btv->pll.pll_crystal) - return; - - if (btv->pll.pll_ofreq == btv->pll.pll_current) { - dprintk("bttv%d: PLL: no change required\n",btv->nr); - return; - } - - if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { - /* no PLL needed */ - if (btv->pll.pll_current == 0) - return; - vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", - btv->nr,btv->pll.pll_ifreq); - btwrite(0x00,BT848_TGCTRL); - btwrite(0x00,BT848_PLL_XCI); - btv->pll.pll_current = 0; - return; - } - - vprintk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->nr, - btv->pll.pll_ifreq, btv->pll.pll_ofreq); - set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); - - for (i=0; i<10; i++) { - /* Let other people run while the PLL stabilizes */ - vprintk("."); - current->state = TASK_INTERRUPTIBLE; - schedule_timeout(HZ/10); - - if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { - btwrite(0,BT848_DSTATUS); - } else { - btwrite(0x08,BT848_TGCTRL); - btv->pll.pll_current = btv->pll.pll_ofreq; - vprintk(" ok\n"); - return; - } - } - btv->pll.pll_current = -1; - vprintk("failed\n"); - return; -} - -static void bt848_muxsel(struct bttv *btv, unsigned int input) -{ - dprintk("bttv%d: bt848_muxsel %d\n",btv->nr,input); - - if (bttv_tvcards[btv->type].muxsel[input] < 0) { - dprintk("bttv%d: digital ccir muxsel\n", btv->nr); - btv->channel = input; - return; - } - - /* needed by RemoteVideo MX */ - if (bttv_tvcards[btv->type].gpiomask2) - btaor(bttv_tvcards[btv->type].gpiomask2, - ~bttv_tvcards[btv->type].gpiomask2, - BT848_GPIO_OUT_EN); - -#if 0 - /* This seems to get rid of some synchronization problems */ - btand(~(3<<5), BT848_IFORM); - mdelay(10); -#endif - - input %= bttv_tvcards[btv->type].video_inputs; - if (input == btv->svhs) { - btor(BT848_CONTROL_COMP, BT848_E_CONTROL); - btor(BT848_CONTROL_COMP, BT848_O_CONTROL); - } else { - btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); - btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); - } - - btaor((bttv_tvcards[btv->type].muxsel[input]&3)<<5, ~(3<<5), BT848_IFORM); - audio(btv, (input!=bttv_tvcards[btv->type].tuner) ? - AUDIO_EXTERN : AUDIO_TUNER); - - if (bttv_tvcards[btv->type].gpiomask2) - btaor(bttv_tvcards[btv->type].muxsel[input]>>4, - ~bttv_tvcards[btv->type].gpiomask2, - BT848_GPIO_DATA); - - /* card specific hook */ - if (bttv_tvcards[btv->type].muxsel_hook) - bttv_tvcards[btv->type].muxsel_hook(btv, input); - - if (bttv_gpio) - bttv_gpio_tracking(btv,"muxsel"); - btv->channel=input; -} +/* ----------------------------------------------------------------------- */ +/* static data */ /* special timing tables from conexant... */ static u8 SRAM_Table[][60] = @@ -461,24 +173,13 @@ } }; -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; - int sram; /* index into SRAM_Table */ -}; - -static struct tvnorm tvnorms[] = { +const struct bttv_tvnorm bttv_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 */ { + .v4l2_id = V4L2_STD_PAL, + .name = "PAL", .Fsc = 35468950, .swidth = 924, .sheight = 576, @@ -489,15 +190,12 @@ .scaledtwidth = 1135, .hdelayx1 = 186, .hactivex1 = 924, -#ifdef VIDEODAT_HACK - .vdelay = VBI_MAXLINES*2, -#else .vdelay = 0x20, -#endif .vbipack = 255, .sram = 0, },{ - /* NTSC */ + .v4l2_id = V4L2_STD_NTSC_M, + .name = "NTSC", .Fsc = 28636363, .swidth = 768, .sheight = 480, @@ -512,7 +210,8 @@ .vbipack = 144, .sram = 1, },{ - /* SECAM L */ + .v4l2_id = V4L2_STD_SECAM, + .name = "SECAM", .Fsc = 35468950, .swidth = 924, .sheight = 576, @@ -527,7 +226,8 @@ .vbipack = 255, .sram = 0, /* like PAL, correct? */ },{ - /* PAL-NC */ + .v4l2_id = V4L2_STD_PAL_Nc, + .name = "PAL-Nc", .Fsc = 28636363, .swidth = 640, .sheight = 576, @@ -542,7 +242,8 @@ .vbipack = 144, .sram = -1, },{ - /* PAL-M */ + .v4l2_id = V4L2_STD_PAL_M, + .name = "PAL-M", .Fsc = 28636363, .swidth = 640, .sheight = 480, @@ -557,8 +258,9 @@ .vbipack = 144, .sram = -1, },{ - /* PAL-N */ - .Fsc = 35468950, + .v4l2_id = V4L2_STD_PAL_N, + .name = "PAL-N", + .Fsc = 35468950, .swidth = 768, .sheight = 576, .totalwidth = 1135, @@ -572,7 +274,8 @@ .vbipack = 144, .sram = -1, },{ - /* NTSC-Japan */ + .v4l2_id = V4L2_STD_NTSC_M_JP, + .name = "NTSC-JP", .Fsc = 28636363, .swidth = 640, .sheight = 480, @@ -587,2241 +290,3236 @@ .vbipack = 144, .sram = -1, },{ - /* Quartzsight digital camera - * From Bt832 datasheet: 393x304 pixel @30Hz, - * Visible: 352x288 pixel - */ - .Fsc = 27000000, - .swidth = 352, - .sheight = 576, //2*288 ? - .totalwidth = 392, - .adelay = 0x68, - .bdelay = 0x5d, - .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), - .scaledtwidth = 392, - .hdelayx1 = 0x20, - .hactivex1 = 352, - .vdelay = 0x08, - .vbipack = 0, //255 - .sram = 2, - } + /* that one hopefully works with the strange timing + * which video recorders produce when playing a NTSC + * tape on a PAL TV ... */ + .v4l2_id = V4L2_STD_PAL_60, + .name = "PAL-60", + .Fsc = 35468950, + .swidth = 924, + .sheight = 480, + .totalwidth = 1135, + .adelay = 0x7f, + .bdelay = 0x72, + .iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), + .scaledtwidth = 1135, + .hdelayx1 = 186, + .hactivex1 = 924, + .vdelay = 0x1a, + .vbipack = 255, + .vtotal = 524, + .sram = -1, + } }; -#define TVNORMS ARRAY_SIZE(tvnorms) - -/* used to switch between the bt848's analog/digital video capture modes */ -void bt848A_set_timing(struct bttv *btv) -{ - int i, len; - int table_idx = tvnorms[btv->win.norm].sram; - int fsc = tvnorms[btv->win.norm].Fsc; - - if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0) { - dprintk("bttv%d: load digital timing table (table_idx=%d)\n", - btv->nr,table_idx); +const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms); - /* timing change...reset timing generator address */ - btwrite(0x00, BT848_TGCTRL); - btwrite(0x02, BT848_TGCTRL); - btwrite(0x00, BT848_TGCTRL); +/* ----------------------------------------------------------------------- */ +/* bttv format list + packed pixel formats must come first */ +const struct bttv_format bttv_formats[] = { + { + .name = "8 bpp, gray", + .palette = VIDEO_PALETTE_GREY, + .fourcc = V4L2_PIX_FMT_GREY, + .btformat = BT848_COLOR_FMT_Y8, + .depth = 8, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "8 bpp, dithered color", + .palette = VIDEO_PALETTE_HI240, + .fourcc = V4L2_PIX_FMT_HI240, + .btformat = BT848_COLOR_FMT_RGB8, + .depth = 8, + .flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER, + },{ + .name = "15 bpp RGB, le", + .palette = VIDEO_PALETTE_RGB555, + .fourcc = V4L2_PIX_FMT_RGB555, + .btformat = BT848_COLOR_FMT_RGB15, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "15 bpp RGB, be", + .palette = -1, + .fourcc = V4L2_PIX_FMT_RGB555X, + .btformat = BT848_COLOR_FMT_RGB15, + .btswap = 0x03, /* byteswap */ + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "16 bpp RGB, le", + .palette = VIDEO_PALETTE_RGB565, + .fourcc = V4L2_PIX_FMT_RGB565, + .btformat = BT848_COLOR_FMT_RGB16, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "16 bpp RGB, be", + .palette = -1, + .fourcc = V4L2_PIX_FMT_RGB565X, + .btformat = BT848_COLOR_FMT_RGB16, + .btswap = 0x03, /* byteswap */ + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "24 bpp RGB, le", + .palette = VIDEO_PALETTE_RGB24, + .fourcc = V4L2_PIX_FMT_BGR24, + .btformat = BT848_COLOR_FMT_RGB24, + .depth = 24, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "32 bpp RGB, le", + .palette = VIDEO_PALETTE_RGB32, + .fourcc = V4L2_PIX_FMT_BGR32, + .btformat = BT848_COLOR_FMT_RGB32, + .depth = 32, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "32 bpp RGB, be", + .palette = -1, + .fourcc = V4L2_PIX_FMT_RGB32, + .btformat = BT848_COLOR_FMT_RGB32, + .btswap = 0x0f, /* byte+word swap */ + .depth = 32, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "4:2:2, packed, YUYV", + .palette = VIDEO_PALETTE_YUV422, + .fourcc = V4L2_PIX_FMT_YUYV, + .btformat = BT848_COLOR_FMT_YUY2, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "4:2:2, packed, YUYV", + .palette = VIDEO_PALETTE_YUYV, + .fourcc = V4L2_PIX_FMT_YUYV, + .btformat = BT848_COLOR_FMT_YUY2, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "4:2:2, packed, UYVY", + .palette = VIDEO_PALETTE_UYVY, + .fourcc = V4L2_PIX_FMT_UYVY, + .btformat = BT848_COLOR_FMT_YUY2, + .btswap = 0x03, /* byteswap */ + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + },{ + .name = "4:2:2, planar, Y-Cb-Cr", + .palette = VIDEO_PALETTE_YUV422P, + .fourcc = V4L2_PIX_FMT_YUV422P, + .btformat = BT848_COLOR_FMT_YCrCb422, + .depth = 16, + .flags = FORMAT_FLAGS_PLANAR, + .hshift = 1, + .vshift = 0, + },{ + .name = "4:2:0, planar, Y-Cb-Cr", + .palette = VIDEO_PALETTE_YUV420P, + .fourcc = V4L2_PIX_FMT_YUV420, + .btformat = BT848_COLOR_FMT_YCrCb422, + .depth = 12, + .flags = FORMAT_FLAGS_PLANAR, + .hshift = 1, + .vshift = 1, + },{ + .name = "4:2:0, planar, Y-Cr-Cb", + .palette = -1, + .fourcc = V4L2_PIX_FMT_YVU420, + .btformat = BT848_COLOR_FMT_YCrCb422, + .depth = 12, + .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb, + .hshift = 1, + .vshift = 1, + },{ + .name = "4:1:1, planar, Y-Cb-Cr", + .palette = VIDEO_PALETTE_YUV411P, + .fourcc = V4L2_PIX_FMT_YUV411P, + .btformat = BT848_COLOR_FMT_YCrCb411, + .depth = 12, + .flags = FORMAT_FLAGS_PLANAR, + .hshift = 2, + .vshift = 0, + },{ + .name = "4:1:0, planar, Y-Cb-Cr", + .palette = VIDEO_PALETTE_YUV410P, + .fourcc = V4L2_PIX_FMT_YUV410, + .btformat = BT848_COLOR_FMT_YCrCb411, + .depth = 9, + .flags = FORMAT_FLAGS_PLANAR, + .hshift = 2, + .vshift = 2, + },{ + .name = "4:1:0, planar, Y-Cr-Cb", + .palette = -1, + .fourcc = V4L2_PIX_FMT_YVU410, + .btformat = BT848_COLOR_FMT_YCrCb411, + .depth = 9, + .flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb, + .hshift = 2, + .vshift = 2, + },{ + .name = "raw scanlines", + .palette = VIDEO_PALETTE_RAW, + .fourcc = -1, + .btformat = BT848_COLOR_FMT_RAW, + .depth = 8, + .flags = FORMAT_FLAGS_RAW, + } +}; +const unsigned int BTTV_FORMATS = ARRAY_SIZE(bttv_formats); - len=SRAM_Table[table_idx][0]; - for(i = 1; i <= len; i++) - btwrite(SRAM_Table[table_idx][i],BT848_TGLB); - btv->pll.pll_ofreq = 27000000; +/* ----------------------------------------------------------------------- */ - set_pll(btv); - btwrite(0x11, BT848_TGCTRL); - btwrite(0x41, BT848_DVSIF); - } else { - btv->pll.pll_ofreq = fsc; - set_pll(btv); - btwrite(0x0, BT848_DVSIF); +#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0) +#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1) +#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2) +#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3) +#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4) +#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5) +#define V4L2_CID_PRIVATE_WHITECRUSH_UPPER (V4L2_CID_PRIVATE_BASE + 6) +#define V4L2_CID_PRIVATE_WHITECRUSH_LOWER (V4L2_CID_PRIVATE_BASE + 7) +#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 8) + +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; +static const struct v4l2_queryctrl bttv_ctls[] = { + /* --- video --- */ + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0, + .maximum = 65535, + .step = 256, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 65535, + .step = 128, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 65535, + .step = 128, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = 0, + .maximum = 65535, + .step = 256, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + /* --- audio --- */ + { + .id = V4L2_CID_AUDIO_MUTE, + .name = "Mute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_AUDIO_VOLUME, + .name = "Volume", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 65535, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_BALANCE, + .name = "Balance", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_BASS, + .name = "Bass", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_AUDIO_TREBLE, + .name = "Treble", + .minimum = 0, + .maximum = 65535, + .step = 65535/100, + .default_value = 32768, + .type = V4L2_CTRL_TYPE_INTEGER, + }, + /* --- private --- */ + { + .id = V4L2_CID_PRIVATE_CHROMA_AGC, + .name = "chroma agc", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_COMBFILTER, + .name = "combfilter", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_AUTOMUTE, + .name = "automute", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_LUMAFILTER, + .name = "luma decimation filter", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_AGC_CRUSH, + .name = "agc crush", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_VCR_HACK, + .name = "vcr hack", + .minimum = 0, + .maximum = 1, + .type = V4L2_CTRL_TYPE_BOOLEAN, + },{ + .id = V4L2_CID_PRIVATE_WHITECRUSH_UPPER, + .name = "whitecrush upper", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0xCF, + .type = V4L2_CTRL_TYPE_INTEGER, + },{ + .id = V4L2_CID_PRIVATE_WHITECRUSH_LOWER, + .name = "whitecrush lower", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 0x7F, + .type = V4L2_CTRL_TYPE_INTEGER, } -} -static void bttv_set_norm(struct bttv *btv, int new_norm) +}; +const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls); + +/* ----------------------------------------------------------------------- */ +/* resource management */ + +static +int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit) { - unsigned long irq_flags; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0 && - bttv_tvcards[btv->type].digital_mode == DIGITAL_MODE_CAMERA) { - //override norm by quartzsight mode - new_norm=7; - dprintk("bttv%d: set_norm fix-up digital: %d\n", - btv->nr, new_norm); - } - - if (btv->win.norm != new_norm) { - btv->win.norm = new_norm; - - make_vbitab(btv); - spin_lock_irqsave(&btv->s_lock, irq_flags); - bt848_set_winsize(btv); - spin_unlock_irqrestore(&btv->s_lock, irq_flags); - - bt848A_set_timing(btv); - switch (btv->type) { - case BTTV_VOODOOTV_FM: - bttv_tda9880_setnorm(btv,new_norm); - break; -#if 0 - case BTTV_OSPREY540: - osprey_540_set_norm(btv,new_norm); - break; -#endif - } + /* is it free? */ + down(&btv->reslock); + if (btv->resources & bit) { + /* no, someone else uses it */ + up(&btv->reslock); + return 0; } + /* it's free, grab it */ + fh->resources |= bit; + btv->resources |= bit; + up(&btv->reslock); + return 1; } -#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 +int check_btres(struct bttv_fh *fh, int bit) +{ + return (fh->resources & bit); +} -static void make_vbitab(struct bttv *btv) +static +int locked_btres(struct bttv *btv, int bit) { - 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)); + return (btv->resources & bit); +} - *(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)); +static +void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) +{ +#if 1 /* DEBUG */ + if ((fh->resources & bits) != bits) { + /* trying to free ressources not allocated by us ... */ + printk("bttv: BUG! (btres)\n"); } - *(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)); +#endif + down(&btv->reslock); + fh->resources &= ~bits; + btv->resources &= ~bits; + up(&btv->reslock); } -static unsigned int fmtbppx2[16] = { - 8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 -}; +/* ----------------------------------------------------------------------- */ +/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */ -static unsigned int palette2fmt[] = { - UNSET, - 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, - UNSET, - UNSET, - UNSET, - BT848_COLOR_FMT_RAW, - BT848_COLOR_FMT_YCrCb422, - BT848_COLOR_FMT_YCrCb411, - BT848_COLOR_FMT_YCrCb422, - BT848_COLOR_FMT_YCrCb411, -}; -#define PALETTEFMT_MAX ARRAY_SIZE(palette2fmt) +/* 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 int make_rawrisctab(struct bttv *btv, u32 *ro, u32 *re, u32 *vbuf) +static void set_pll_freq(struct bttv *btv, unsigned int fin, unsigned int fout) { - u32 line; - u32 bpl=1024; /* bytes per line */ - unsigned long vadr=(unsigned long) vbuf; + unsigned char fl, fh, fi; + + /* prevent overflows */ + fin/=4; + fout/=4; - *(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 ... */ + fout*=12; + fi=fout/fin; - 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; + fout=(fout%fin)*256; + fh=fout/fin; + + fout=(fout%fin)*256; + fl=fout/fin; + + btwrite(fl, BT848_PLL_F_LO); + btwrite(fh, BT848_PLL_F_HI); + btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); } -static int make_prisctab(struct bttv *btv, u32 *ro, u32 *re, u32 *vbuf, - u16 width, u16 height, u16 fmt) +static void set_pll(struct bttv *btv) { - u16 line, lmask; - u32 bl, blcr, blcb, rcmd; - u32 todo; - u32 **rp; - int inter; - unsigned long cbadr, cradr; - unsigned long vadr=(unsigned long) vbuf; - u32 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; + int i; - 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)); + if (!btv->pll.pll_crystal) + return; - return 0; + if (btv->pll.pll_ofreq == btv->pll.pll_current) { + dprintk("bttv%d: PLL: no change required\n",btv->c.nr); + return; + } + + if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { + /* no PLL needed */ + if (btv->pll.pll_current == 0) + return; + vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", + btv->c.nr,btv->pll.pll_ifreq); + btwrite(0x00,BT848_TGCTRL); + btwrite(0x00,BT848_PLL_XCI); + btv->pll.pll_current = 0; + return; + } + + vprintk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->c.nr, + btv->pll.pll_ifreq, btv->pll.pll_ofreq); + set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); + + for (i=0; i<10; i++) { + /* Let other people run while the PLL stabilizes */ + vprintk("."); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ/50); + + if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { + btwrite(0,BT848_DSTATUS); + } else { + btwrite(0x08,BT848_TGCTRL); + btv->pll.pll_current = btv->pll.pll_ofreq; + vprintk(" ok\n"); + return; + } + } + btv->pll.pll_current = -1; + vprintk("failed\n"); + return; } -static int make_vrisctab(struct bttv *btv, u32 *ro, u32 *re, u32 *vbuf, - u16 width, u16 height, u16 palette) +/* used to switch between the bt848's analog/digital video capture modes */ +void bt848A_set_timing(struct bttv *btv) { - u16 line; - u32 bpl; /* bytes per line */ - u32 bl; - u32 todo; - u32 **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); + int i, len; + int table_idx = bttv_tvnorms[btv->tvnorm].sram; + int fsc = bttv_tvnorms[btv->tvnorm].Fsc; - 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; + if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) { + dprintk("bttv%d: load digital timing table (table_idx=%d)\n", + btv->c.nr,table_idx); + + /* timing change...reset timing generator address */ + btwrite(0x00, BT848_TGCTRL); + btwrite(0x02, BT848_TGCTRL); + btwrite(0x00, BT848_TGCTRL); + + len=SRAM_Table[table_idx][0]; + for(i = 1; i <= len; i++) + btwrite(SRAM_Table[table_idx][i],BT848_TGLB); + btv->pll.pll_ofreq = 27000000; + + set_pll(btv); + btwrite(0x11, BT848_TGCTRL); + btwrite(0x41, BT848_DVSIF); + } else { + btv->pll.pll_ofreq = fsc; + set_pll(btv); + btwrite(0x0, BT848_DVSIF); + } } -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) +static void bt848_bright(struct bttv *btv, int bright) { - unsigned char lmask, rmask, *p; - int W, l, r; - int i; + int value; - 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; - } - + // printk("bttv: set bright: %d\n",bright); // DEBUG + btv->bright = bright; + /* We want -128 to 127 we get 0-65535 */ + value = (bright >> 8) - 128; + btwrite(value & 0xff, BT848_BRIGHT); } -static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr) +static void bt848_hue(struct bttv *btv, int hue) { - u32 line, x, y, bpl, width, height, inter, maxw; - u32 bpp, dx, sx, **rp, *ro, *re, flags, len; - u32 adr; - int i; - unsigned char *clipmap, *clipline, cbit, lastbit, outofmem; + int value; + + btv->hue = hue; - /* 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); - return; - } - 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); + /* -128 to 127 */ + value = (hue >> 8) - 128; + btwrite(value & 0xff, BT848_HUE); +} + +static void bt848_contrast(struct bttv *btv, int cont) +{ + int value,hibit; - /* 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); + btv->contrast = cont; + + /* 0-511 */ + value = (cont >> 7); + hibit = (value >> 6) & 4; + btwrite(value & 0xff, BT848_CONTRAST_LO); + btaor(hibit, ~4, BT848_E_CONTROL); + btaor(hibit, ~4, BT848_O_CONTROL); } -/* - * 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; - - int swidth = tvn->swidth; - int totalwidth = tvn->totalwidth; - int scaledtwidth = tvn->scaledtwidth; - - if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0) { - dprintk("bttv%d: DIGITAL_MODE_VIDEO override width\n",btv->nr); - swidth = 720; - totalwidth = 858; - scaledtwidth = 858; - } - - xsf = (width*scaledtwidth)/swidth; - hscale = ((totalwidth*4096UL)/xsf-4096); - hdelay = tvn->hdelayx1; - hdelay = (hdelay*width)/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); - } +static void bt848_sat(struct bttv *btv, int color) +{ + int val_u,val_v,hibits; + + btv->saturation = color; - 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); + /* 0-511 for the color */ + val_u = color >> 7; + val_v = ((color>>7)*180L)/254; + hibits = (val_u >> 7) & 2; + hibits |= (val_v >> 8) & 1; + btwrite(val_u & 0xff, BT848_SAT_U_LO); + btwrite(val_v & 0xff, BT848_SAT_V_LO); + btaor(hibits, ~3, BT848_E_CONTROL); + btaor(hibits, ~3, BT848_O_CONTROL); } +/* ----------------------------------------------------------------------- */ -static void bt848_set_geo(struct bttv *btv) +static int +video_mux(struct bttv *btv, unsigned int input) { - 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); - if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0) - btwrite(0x39, BT848_VBI_PACK_DEL); - else - btwrite(0x01, BT848_VBI_PACK_DEL); + int mux,mask2; - btv->pll.pll_ofreq = tvn->Fsc; - if (!in_interrupt()) - set_pll(btv); + if (input >= bttv_tvcards[btv->c.type].video_inputs) + return -EINVAL; - btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0; + /* needed by RemoteVideo MX */ + mask2 = bttv_tvcards[btv->c.type].gpiomask2; + if (mask2) + gpio_inout(mask2,mask2); - 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; + if (input == btv->svhs) { + btor(BT848_CONTROL_COMP, BT848_E_CONTROL); + btor(BT848_CONTROL_COMP, BT848_O_CONTROL); } 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; + btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); + btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); } + mux = bttv_tvcards[btv->c.type].muxsel[input] & 3; + btaor(mux<<5, ~(3<<5), BT848_IFORM); + dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n", + btv->c.nr,input,mux); - /* 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); + /* card specific hook */ + if(bttv_tvcards[btv->c.type].muxsel_hook) + bttv_tvcards[btv->c.type].muxsel_hook (btv, input); + return 0; } - -static int bpp2fmt[4] = { - BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, - BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 +static char *audio_modes[] = { + "audio: tuner", "audio: radio", "audio: extern", + "audio: intern", "audio: off" }; -static void bt848_set_winsize(struct bttv *btv) +static int +audio_mux(struct bttv *btv, int mode) { - unsigned short format; + int val,mux,i2c_mux,signal; - 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); + gpio_inout(bttv_tvcards[btv->c.type].gpiomask, + bttv_tvcards[btv->c.type].gpiomask); + signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC; + + switch (mode) { + case AUDIO_MUTE: + btv->audio |= AUDIO_MUTE; + break; + case AUDIO_UNMUTE: + btv->audio &= ~AUDIO_MUTE; + break; + case AUDIO_TUNER: + case AUDIO_RADIO: + case AUDIO_EXTERN: + case AUDIO_INTERN: + btv->audio &= AUDIO_MUTE; + btv->audio |= mode; + } + i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio; + if (btv->opt_automute && !signal && !btv->radio_user) + mux = AUDIO_OFF; +#if 0 + printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n", + btv->c.nr, mode, btv->audio, signal ? "yes" : "no", + mux, i2c_mux, in_interrupt() ? "yes" : "no"); +#endif - bt848_set_geo(btv); + val = bttv_tvcards[btv->c.type].audiomux[mux]; + gpio_bits(bttv_tvcards[btv->c.type].gpiomask,val); + if (bttv_gpio) + bttv_gpio_tracking(btv,audio_modes[mux]); + if (!in_interrupt()) + bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux)); + return 0; } -/* - * Grab into virtual memory. - */ +static void +i2c_vidiocschan(struct bttv *btv) +{ + struct video_channel c; + + memset(&c,0,sizeof(c)); + c.norm = btv->tvnorm; + c.channel = btv->input; + bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c); + if (btv->c.type == BTTV_VOODOOTV_FM) + bttv_tda9880_setnorm(btv,c.norm); +} -static int vgrab(struct bttv *btv, struct video_mmap *mp) +static int +set_tvnorm(struct bttv *btv, unsigned int norm) { - u32 *ro, *re; - u32 *vbuf; - unsigned long flags; - - if(btv->fbuffer==NULL) - { - if(fbuffer_alloc(btv)) - return -ENOBUFS; - } + const struct bttv_tvnorm *tvnorm; - 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) + if (norm < 0 || norm >= BTTV_TVNORMS) return -EINVAL; - if ((unsigned int)mp->height * mp->width * - fmtbppx2[palette2fmt[mp->format]&0x0f]/2 > gbufsize) - return -EINVAL; - if (UNSET == palette2fmt[mp->format]) - return -EINVAL; + btv->tvnorm = norm; + tvnorm = &bttv_tvnorms[norm]; - /* - * 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; + btwrite(tvnorm->adelay, BT848_ADELAY); + btwrite(tvnorm->bdelay, BT848_BDELAY); + btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), + BT848_IFORM); + btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE); + btwrite(1, BT848_VBI_PACK_DEL); + bt848A_set_timing(btv); + + switch (btv->c.type) { + case BTTV_VOODOOTV_FM: + bttv_tda9880_setnorm(btv,norm); + break; +#if 0 + case BTTV_OSPREY540: + osprey_540_set_norm(btv,norm); + break; #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) +static void +set_input(struct bttv *btv, unsigned int input) { - return -EINVAL; + unsigned long flags; + + btv->input = input; + if (irq_iswitch) { + spin_lock_irqsave(&btv->s_lock,flags); + if (btv->curr.irqflags) { + /* active capture -> delayed input switch */ + btv->new_input = input; + } else { + video_mux(btv,input); + } + spin_unlock_irqrestore(&btv->s_lock,flags); + } else { + video_mux(btv,input); + } + audio_mux(btv,(input == bttv_tvcards[btv->c.type].tuner ? + AUDIO_TUNER : AUDIO_EXTERN)); + set_tvnorm(btv,btv->tvnorm); } -static long bttv_read(struct video_device *v, char *buf, unsigned long count, int nonblock) +static void init_irqreg(struct bttv *btv) { - /* use bttv 0.9.x if you need capture via read() */ - return -EINVAL; -} + /* clear status */ + btwrite(0xfffffUL, BT848_INT_STAT); -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); + if (bttv_tvcards[btv->c.type].no_video) { + /* i2c only */ + btwrite(BT848_INT_I2CDONE, + BT848_INT_MASK); + } else { + /* full video */ + btwrite((btv->triton1) | + (btv->gpioirq ? BT848_INT_GPINT : 0) | + BT848_INT_SCERR | + (fdsr ? BT848_INT_FDSR : 0) | + BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| + BT848_INT_FMTCHG|BT848_INT_HLOCK| + BT848_INT_I2CDONE, + BT848_INT_MASK); + } } -/* - * 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) +static void init_bt848(struct bttv *btv) { - unsigned 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); + int val; + + if (bttv_tvcards[btv->c.type].no_video) { + /* very basic init only */ + init_irqreg(btv); + return; + } - /* flag the chip needs a restart */ - btv->needs_restart = 1; - spin_unlock(&btv->s_lock); + btwrite(0x00, BT848_CAP_CTL); + btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); + btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM); - wake_up_interruptible(&btv->vbiq); - wake_up_interruptible(&btv->capq); -} + /* 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); -static void bt848_restart(struct bttv *btv) + val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; + btwrite(val, BT848_E_SCLOOP); + btwrite(val, BT848_O_SCLOOP); + + btwrite(0x20, BT848_E_VSCALE_HI); + btwrite(0x20, BT848_O_VSCALE_HI); + btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), + BT848_ADC); + + btwrite(whitecrush_upper, BT848_WC_UP); + btwrite(whitecrush_lower, BT848_WC_DOWN); + + bt848_bright(btv, btv->bright); + bt848_hue(btv, btv->hue); + bt848_contrast(btv, btv->contrast); + bt848_sat(btv, btv->saturation); + + if (btv->opt_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); + } + + /* interrupt */ + init_irqreg(btv); +} + +extern void bttv_reinit_bt848(struct bttv *btv) { - unsigned long irq_flags; + unsigned long 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 = -1; - set_pll(btv); - - spin_lock_irqsave(&btv->s_lock, irq_flags); - btv->errors = 0; - btv->needs_restart = 0; - bt848_set_geo(btv); - bt848_set_risc_jmps(btv,-1); - spin_unlock_irqrestore(&btv->s_lock, irq_flags); -} + printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->c.nr); + spin_lock_irqsave(&btv->s_lock,flags); + btv->errors=0; + bttv_set_dma(btv,0,0); + spin_unlock_irqrestore(&btv->s_lock,flags); -/* - * Open a bttv card. Right now the flags stuff is just playing - */ + init_bt848(btv); + btv->pll.pll_current = -1; + set_input(btv,btv->input); +} -static int bttv_open(struct video_device *dev, int flags) +static int get_control(struct bttv *btv, struct v4l2_control *c) { - struct bttv *btv = (struct bttv *)dev; - unsigned int i; - int ret; - - ret = -EBUSY; - if (bttv_debug) - printk("bttv%d: open called\n",btv->nr); - - down(&btv->lock); - if (btv->user) - goto out_unlock; + struct video_audio va; + int i; - 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; + for (i = 0; i < BTTV_CTLS; i++) + if (bttv_ctls[i].id == c->id) + break; + if (i == BTTV_CTLS) + return -EINVAL; + if (i >= 4 && i <= 8) { + memset(&va,0,sizeof(va)); + bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); + if (btv->audio_hook) + btv->audio_hook(btv,&va,0); + } + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = btv->bright; + break; + case V4L2_CID_HUE: + c->value = btv->hue; + break; + case V4L2_CID_CONTRAST: + c->value = btv->contrast; + break; + case V4L2_CID_SATURATION: + c->value = btv->saturation; + break; - out_unlock: - up(&btv->lock); - return ret; + case V4L2_CID_AUDIO_MUTE: + c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; + break; + case V4L2_CID_AUDIO_VOLUME: + c->value = va.volume; + break; + case V4L2_CID_AUDIO_BALANCE: + c->value = va.balance; + break; + case V4L2_CID_AUDIO_BASS: + c->value = va.bass; + break; + case V4L2_CID_AUDIO_TREBLE: + c->value = va.treble; + break; + + case V4L2_CID_PRIVATE_CHROMA_AGC: + c->value = btv->opt_chroma_agc; + break; + case V4L2_CID_PRIVATE_COMBFILTER: + c->value = btv->opt_combfilter; + break; + case V4L2_CID_PRIVATE_LUMAFILTER: + c->value = btv->opt_lumafilter; + break; + case V4L2_CID_PRIVATE_AUTOMUTE: + c->value = btv->opt_automute; + break; + case V4L2_CID_PRIVATE_AGC_CRUSH: + c->value = btv->opt_adc_crush; + break; + case V4L2_CID_PRIVATE_VCR_HACK: + c->value = btv->opt_vcr_hack; + break; + case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: + c->value = btv->opt_whitecrush_upper; + break; + case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: + c->value = btv->opt_whitecrush_lower; + break; + default: + return -EINVAL; + } + return 0; } -static void bttv_close(struct video_device *dev) +static int set_control(struct bttv *btv, struct v4l2_control *c) { - struct bttv *btv=(struct bttv *)dev; - unsigned long irq_flags; - int need_wait; + struct video_audio va; + int i,val; - 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); + for (i = 0; i < BTTV_CTLS; i++) + if (bttv_ctls[i].id == c->id) + break; + if (i == BTTV_CTLS) + return -EINVAL; + if (i >= 4 && i <= 8) { + memset(&va,0,sizeof(va)); + bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); + if (btv->audio_hook) + btv->audio_hook(btv,&va,0); + } + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + bt848_bright(btv,c->value); + break; + case V4L2_CID_HUE: + bt848_hue(btv,c->value); + break; + case V4L2_CID_CONTRAST: + bt848_contrast(btv,c->value); + break; + case V4L2_CID_SATURATION: + bt848_sat(btv,c->value); + break; + case V4L2_CID_AUDIO_MUTE: + if (c->value) { + va.flags |= VIDEO_AUDIO_MUTE; + audio_mux(btv, AUDIO_MUTE); + } else { + va.flags &= ~VIDEO_AUDIO_MUTE; + audio_mux(btv, AUDIO_UNMUTE); + } + break; + + case V4L2_CID_AUDIO_VOLUME: + va.volume = c->value; + break; + case V4L2_CID_AUDIO_BALANCE: + va.balance = c->value; + break; + case V4L2_CID_AUDIO_BASS: + va.bass = c->value; + break; + case V4L2_CID_AUDIO_TREBLE: + va.treble = c->value; + break; + + case V4L2_CID_PRIVATE_CHROMA_AGC: + btv->opt_chroma_agc = c->value; + val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; + btwrite(val, BT848_E_SCLOOP); + btwrite(val, BT848_O_SCLOOP); + break; + case V4L2_CID_PRIVATE_COMBFILTER: + btv->opt_combfilter = c->value; + break; + case V4L2_CID_PRIVATE_LUMAFILTER: + btv->opt_lumafilter = c->value; + if (btv->opt_lumafilter) { + btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL); + btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL); + } else { + btor(BT848_CONTROL_LDEC, BT848_E_CONTROL); + btor(BT848_CONTROL_LDEC, BT848_O_CONTROL); + } + break; + case V4L2_CID_PRIVATE_AUTOMUTE: + btv->opt_automute = c->value; + break; + case V4L2_CID_PRIVATE_AGC_CRUSH: + btv->opt_adc_crush = c->value; + btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), + BT848_ADC); + break; + case V4L2_CID_PRIVATE_VCR_HACK: + btv->opt_vcr_hack = c->value; + break; + case V4L2_CID_PRIVATE_WHITECRUSH_UPPER: + btv->opt_whitecrush_upper = c->value; + btwrite(c->value, BT848_WC_UP); + break; + case V4L2_CID_PRIVATE_WHITECRUSH_LOWER: + btv->opt_whitecrush_lower = c->value; + btwrite(c->value, BT848_WC_DOWN); + break; + default: + return -EINVAL; + } + if (i >= 4 && i <= 8) { + bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); + if (btv->audio_hook) + btv->audio_hook(btv,&va,1); + } + return 0; } +/* ----------------------------------------------------------------------- */ -/***********************************/ -/* ioctls and supporting functions */ -/***********************************/ +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->c.nr,outbits,data & outbits, data & ~outbits, comment); +} -static inline void bt848_bright(struct bttv *btv, uint bright) +void bttv_field_count(struct bttv *btv) { - btwrite(bright&0xff, BT848_BRIGHT); + int need_count = 0; + + if (btv->users) + need_count++; + + if (need_count) { + /* start field counter */ + btor(BT848_INT_VSYNC,BT848_INT_MASK); + } else { + /* stop field counter */ + btand(~BT848_INT_VSYNC,BT848_INT_MASK); + btv->field_count = 0; + } } -static inline void bt848_hue(struct bttv *btv, uint hue) +static const struct bttv_format* +format_by_palette(int palette) { - btwrite(hue&0xff, BT848_HUE); + unsigned int i; + + for (i = 0; i < BTTV_FORMATS; i++) { + if (-1 == bttv_formats[i].palette) + continue; + if (bttv_formats[i].palette == palette) + return bttv_formats+i; + } + return NULL; } -static inline void bt848_contrast(struct bttv *btv, uint cont) +static const struct bttv_format* +format_by_fourcc(int fourcc) { - unsigned int conthi; + unsigned int i; - conthi=(cont>>6)&4; - btwrite(cont&0xff, BT848_CONTRAST_LO); - btaor(conthi, ~4, BT848_E_CONTROL); - btaor(conthi, ~4, BT848_O_CONTROL); + for (i = 0; i < BTTV_FORMATS; i++) { + if (-1 == bttv_formats[i].fourcc) + continue; + if (bttv_formats[i].fourcc == fourcc) + return bttv_formats+i; + } + return NULL; } -static inline void bt848_sat_u(struct bttv *btv, unsigned long data) +/* ----------------------------------------------------------------------- */ +/* misc helpers */ + +static int +bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, + struct bttv_buffer *new) { - u32 datahi; + struct bttv_buffer *old; + unsigned long flags; + int retval = 0; - datahi=(data>>7)&2; - btwrite(data&0xff, BT848_SAT_U_LO); - btaor(datahi, ~2, BT848_E_CONTROL); - btaor(datahi, ~2, BT848_O_CONTROL); + dprintk("switch_overlay: enter [new=%p]\n",new); + if (new) + new->vb.state = STATE_DONE; + spin_lock_irqsave(&btv->s_lock,flags); + old = btv->screen; + btv->screen = new; + btv->curr.irqflags |= 1; + bttv_set_dma(btv, 0x03, btv->curr.irqflags); + spin_unlock_irqrestore(&btv->s_lock,flags); + if (NULL == new) + free_btres(btv,fh,RESOURCE_OVERLAY); + if (NULL != old) { + dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state); + bttv_dma_free(btv, old); + kfree(old); + } + dprintk("switch_overlay: done\n"); + return retval; } -static inline void bt848_sat_v(struct bttv *btv, unsigned long data) +/* ----------------------------------------------------------------------- */ +/* video4linux (1) interface */ + +static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, + const struct bttv_format *fmt, + unsigned int width, unsigned int height, + enum v4l2_field field) { - u32 datahi; + int redo_dma_risc = 0; + int rc; + + /* check settings */ + if (NULL == fmt) + return -EINVAL; + if (fmt->btformat == BT848_COLOR_FMT_RAW) { + width = RAW_BPL; + height = RAW_LINES*2; + if (width*height > buf->vb.bsize) + return -EINVAL; + buf->vb.size = buf->vb.bsize; + } else { + if (width < 48 || + height < 32 || + width > bttv_tvnorms[btv->tvnorm].swidth || + height > bttv_tvnorms[btv->tvnorm].sheight) + return -EINVAL; + buf->vb.size = (width * height * fmt->depth) >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + } + + /* alloc + fill struct bttv_buffer (if changed) */ + if (buf->vb.width != width || buf->vb.height != height || + buf->vb.field != field || + buf->tvnorm != btv->tvnorm || buf->fmt != fmt) { + buf->vb.width = width; + buf->vb.height = height; + buf->vb.field = field; + buf->tvnorm = btv->tvnorm; + buf->fmt = fmt; + redo_dma_risc = 1; + } + + /* alloc risc memory */ + if (STATE_NEEDS_INIT == buf->vb.state) { + redo_dma_risc = 1; + if (0 != (rc = videobuf_iolock(btv->c.pci,&buf->vb,&btv->fbuf))) + goto fail; + } + + if (redo_dma_risc) + if (0 != (rc = bttv_buffer_risc(btv,buf))) + goto fail; + + buf->vb.state = STATE_PREPARED; + return 0; - datahi=(data>>8)&1; - btwrite(data&0xff, BT848_SAT_V_LO); - btaor(datahi, ~1, BT848_E_CONTROL); - btaor(datahi, ~1, BT848_O_CONTROL); + fail: + bttv_dma_free(btv,buf); + return rc; } -/* - * ioctl routine - */ +static int +buffer_setup(struct file *file, unsigned int *count, unsigned int *size) +{ + struct bttv_fh *fh = file->private_data; + + *size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 == *count) + *count = gbuffers; + while (*size * *count > gbuffers * gbufsize) + (*count)--; + return 0; +} -static int bttv_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +static int +buffer_prepare(struct file *file, struct videobuf_buffer *vb, + enum v4l2_field field) { - struct bttv *btv=(struct bttv *)dev; - unsigned long irq_flags; - int ret = 0; + struct bttv_buffer *buf = (struct bttv_buffer*)vb; + struct bttv_fh *fh = file->private_data; - if (bttv_debug > 1) - printk("bttv%d: ioctl 0x%x\n",btv->nr,cmd); + return bttv_prepare_buffer(fh->btv, buf, fh->fmt, + fh->width, fh->height, field); +} +static void +buffer_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct bttv_buffer *buf = (struct bttv_buffer*)vb; + struct bttv_fh *fh = file->private_data; + + buf->vb.state = STATE_QUEUED; + list_add_tail(&buf->vb.queue,&fh->btv->capture); + fh->btv->curr.irqflags |= 1; + bttv_set_dma(fh->btv, 0x03, fh->btv->curr.irqflags); +} + +static void buffer_release(struct file *file, struct videobuf_buffer *vb) +{ + struct bttv_buffer *buf = (struct bttv_buffer*)vb; + struct bttv_fh *fh = file->private_data; + + bttv_dma_free(fh->btv,buf); +} + +static struct videobuf_queue_ops bttv_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static const char *v4l1_ioctls[] = { + "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", + "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", + "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", + "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", + "SMICROCODE", "GVBIFMT", "SVBIFMT" }; +#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) + +int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) +{ switch (cmd) { - case VIDIOCGCAP: + case BTTV_VERSION: + return BTTV_VERSION_CODE; + + /* *** v4l1 *** ************************************************ */ + case VIDIOCGFREQ: { - struct video_capability b; - strcpy(b.name,btv->video_dev.name); - b.type = VID_TYPE_CAPTURE| - ((bttv_tvcards[btv->type].tuner != UNSET) ? 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; + unsigned long *freq = arg; + *freq = btv->freq; return 0; } - case VIDIOCGCHAN: + case VIDIOCSFREQ: { - struct video_channel v; - unsigned int channel; + unsigned long *freq = arg; + down(&btv->lock); + btv->freq=*freq; + bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq); + if (btv->has_matchbox && btv->radio_user) + tea5757_set_freq(btv,*freq); + up(&btv->lock); + return 0; + } - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - channel = v.channel; - if (channel>=bttv_tvcards[btv->type].video_inputs) - return -EINVAL; - v.flags=VIDEO_VC_AUDIO; - v.tuners=0; - v.type=VIDEO_TYPE_CAMERA; - v.norm = btv->win.norm; - if(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 (channel == btv->svhs) - strcpy(v.name,"S-Video"); - else if (bttv_tvcards[btv->type].muxsel[v.channel] < 0) - strcpy(v.name,"Digital Video"); - else - sprintf(v.name,"Composite%d",v.channel); + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; + if (UNSET == bttv_tvcards[btv->c.type].tuner) + return -EINVAL; + if (v->tuner) /* Only tuner 0 */ + return -EINVAL; + strcpy(v->name, "Television"); + v->rangelow = 0; + v->rangehigh = 0x7FFFFFFF; + v->flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; + v->mode = btv->tvnorm; + v->signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; + bttv_call_i2c_clients(btv,cmd,v); return 0; } - /* - * Each channel has 1 tuner - */ - case VIDIOCSCHAN: + case VIDIOCSTUNER: { - struct video_channel v; - unsigned int channel; + struct video_tuner *v = arg; - if(copy_from_user(&v, arg,sizeof(v))) - return -EFAULT; - channel = v.channel; - - if (channel>bttv_tvcards[btv->type].video_inputs) + if (v->tuner) /* Only tuner 0 */ + return -EINVAL; + if (v->mode >= BTTV_TVNORMS) return -EINVAL; - if (v.norm > TVNORMS) - return -EOPNOTSUPP; - bttv_call_i2c_clients(btv,cmd,&v); down(&btv->lock); - bt848_muxsel(btv, channel); - bttv_set_norm(btv, v.norm); + set_tvnorm(btv,v->mode); + bttv_call_i2c_clients(btv,cmd,v); up(&btv->lock); return 0; } - case VIDIOCGTUNER: + + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + unsigned int channel = v->channel; + + if (channel >= bttv_tvcards[btv->c.type].video_inputs) + return -EINVAL; + v->tuners=0; + v->flags = VIDEO_VC_AUDIO; + v->type = VIDEO_TYPE_CAMERA; + v->norm = btv->tvnorm; + if (channel == bttv_tvcards[btv->c.type].tuner) { + strcpy(v->name,"Television"); + v->flags|=VIDEO_VC_TUNER; + v->type=VIDEO_TYPE_TV; + v->tuners=1; + } else if (channel == btv->svhs) { + strcpy(v->name,"S-Video"); + } else { + sprintf(v->name,"Composite%d",channel); + } + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + unsigned int channel = v->channel; + + if (channel >= bttv_tvcards[btv->c.type].video_inputs) + return -EINVAL; + if (v->norm >= BTTV_TVNORMS) + return -EINVAL; + + down(&btv->lock); + if (channel == btv->input && + v->norm == btv->tvnorm) { + /* nothing to do */ + up(&btv->lock); + return 0; + } + + btv->tvnorm = v->norm; + set_input(btv,v->channel); + up(&btv->lock); + return 0; + } + + case VIDIOCGAUDIO: { - 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 */ + struct video_audio *v = arg; + + memset(v,0,sizeof(*v)); + strcpy(v->name,"Television"); + v->flags |= VIDEO_AUDIO_MUTABLE; + v->mode = VIDEO_SOUND_MONO; + + down(&btv->lock); + bttv_call_i2c_clients(btv,cmd,v); + + /* card specific hooks */ + if (btv->audio_hook) + btv->audio_hook(btv,v,0); + + up(&btv->lock); + return 0; + } + case VIDIOCSAUDIO: + { + struct video_audio *v = arg; + unsigned int audio = v->audio; + + if (audio >= bttv_tvcards[btv->c.type].audio_inputs) 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; + + down(&btv->lock); + audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE); + bttv_call_i2c_clients(btv,cmd,v); + + /* card specific hooks */ + if (btv->audio_hook) + btv->audio_hook(btv,v,1); + + up(&btv->lock); return 0; } - /* We have but one tuner */ - case VIDIOCSTUNER: + + /* *** v4l2 *** ************************************************ */ + case VIDIOC_ENUMSTD: { - struct video_tuner v; - unsigned int tuner; + struct v4l2_standard *e = arg; + unsigned int index = e->index; - if(copy_from_user(&v, arg, sizeof(v))) - return -EFAULT; - tuner = v.tuner; - /* Only one channel has a tuner */ - if(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) { - down(&btv->lock); - bttv_set_norm(btv,v.mode); - up(&btv->lock); - } + if (index >= BTTV_TVNORMS) + return -EINVAL; + v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id, + bttv_tvnorms[e->index].name); + e->index = index; return 0; } - case VIDIOCGPICT: + case VIDIOC_G_STD: { - struct video_picture p=btv->picture; - if(copy_to_user(arg, &p, sizeof(p))) - return -EFAULT; + v4l2_std_id *id = arg; + *id = bttv_tvnorms[btv->tvnorm].v4l2_id; return 0; } - case VIDIOCSPICT: + case VIDIOC_S_STD: { - struct video_picture p; + v4l2_std_id *id = arg; + unsigned int i; - if (copy_from_user(&p, arg,sizeof(p))) - return -EFAULT; - if (p.palette > PALETTEFMT_MAX) + for (i = 0; i < BTTV_TVNORMS; i++) + if (*id & bttv_tvnorms[i].v4l2_id) + break; + if (i == BTTV_TVNORMS) return -EINVAL; - if (UNSET == palette2fmt[p.palette]) + + down(&btv->lock); + set_tvnorm(btv,i); + i2c_vidiocschan(btv); + up(&btv->lock); + return 0; + } + case VIDIOC_QUERYSTD: + { + v4l2_std_id *id = arg; + + if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML) + *id = V4L2_STD_625_50; + else + *id = V4L2_STD_525_60; + return 0; + } + + case VIDIOC_ENUMINPUT: + { + struct v4l2_input *i = arg; + unsigned int n; + + n = i->index; + if (n >= bttv_tvcards[btv->c.type].video_inputs) + return -EINVAL; + memset(i,0,sizeof(*i)); + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + i->audioset = 1; + if (i->index == bttv_tvcards[btv->c.type].tuner) { + sprintf(i->name, "Television"); + i->type = V4L2_INPUT_TYPE_TUNER; + i->tuner = 0; + } else if (i->index == btv->svhs) { + sprintf(i->name, "S-Video"); + } else { + sprintf(i->name,"Composite%d",i->index); + } + if (i->index == btv->input) { + __u32 dstatus = btread(BT848_DSTATUS); + if (0 == (dstatus & BT848_DSTATUS_PRES)) + i->status |= V4L2_IN_ST_NO_SIGNAL; + if (0 == (dstatus & BT848_DSTATUS_HLOC)) + i->status |= V4L2_IN_ST_NO_H_LOCK; + } + for (n = 0; n < BTTV_TVNORMS; n++) + i->std |= bttv_tvnorms[n].v4l2_id; + return 0; + } + case VIDIOC_G_INPUT: + { + int *i = arg; + *i = btv->input; + return 0; + } + case VIDIOC_S_INPUT: + { + unsigned int *i = arg; + + if (*i > bttv_tvcards[btv->c.type].video_inputs) 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; + set_input(btv,*i); + i2c_vidiocschan(btv); up(&btv->lock); return 0; } - case VIDIOCSWIN: + + case VIDIOC_G_TUNER: { - struct video_window vw; - struct video_clip *vcp = NULL; - - if(copy_from_user(&vw,arg,sizeof(vw))) - return -EFAULT; + struct v4l2_tuner *t = arg; + if (UNSET == bttv_tvcards[btv->c.type].tuner) + return -EINVAL; + if (0 != t->index) + return -EINVAL; down(&btv->lock); - if(vw.flags || vw.width < 16 || vw.height < 16) + memset(t,0,sizeof(*t)); + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->rangehigh = 0xffffffffUL; + t->capability = V4L2_TUNER_CAP_NORM; + t->rxsubchans = V4L2_TUNER_SUB_MONO; + if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) + t->signal = 0xffff; { - 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; + /* Hmmm ... */ + struct video_audio va; + memset(&va, 0, sizeof(struct video_audio)); + bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); + if (btv->audio_hook) + btv->audio_hook(btv,&va,0); + if(va.mode & VIDEO_SOUND_STEREO) { + t->audmode = V4L2_TUNER_MODE_STEREO; + t->rxsubchans |= V4L2_TUNER_SUB_STEREO; } - if(copy_from_user(vcp, vw.clips, - VIDEO_CLIPMAP_SIZE)) { - up(&btv->lock); - vfree(vcp); - return -EFAULT; + if(va.mode & VIDEO_SOUND_LANG1) { + t->audmode = V4L2_TUNER_MODE_LANG1; + t->rxsubchans = V4L2_TUNER_SUB_LANG1 + | V4L2_TUNER_SUB_LANG2; } - } else if (vw.clipcount > 2048) { - up(&btv->lock); + } + /* FIXME: fill capability+audmode */ + up(&btv->lock); + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *t = arg; + + if (UNSET == bttv_tvcards[btv->c.type].tuner) 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; - } + if (0 != t->index) + return -EINVAL; + down(&btv->lock); + { + struct video_audio va; + memset(&va, 0, sizeof(struct video_audio)); + bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); + if (t->audmode == V4L2_TUNER_MODE_MONO) + va.mode = VIDEO_SOUND_MONO; + else if (t->audmode == V4L2_TUNER_MODE_STEREO) + va.mode = VIDEO_SOUND_STEREO; + else if (t->audmode == V4L2_TUNER_MODE_LANG1) + va.mode = VIDEO_SOUND_LANG1; + else if (t->audmode == V4L2_TUNER_MODE_LANG2) + va.mode = VIDEO_SOUND_LANG2; + bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); + if (btv->audio_hook) + btv->audio_hook(btv,&va,1); } - 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: + + case VIDIOC_G_FREQUENCY: { - 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; + struct v4l2_frequency *f = arg; + + memset(f,0,sizeof(*f)); + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = btv->freq; return 0; } - case VIDIOCCAPTURE: + case VIDIOC_S_FREQUENCY: { - int v; - if(copy_from_user(&v, arg,sizeof(v))) + struct v4l2_frequency *f = arg; + + if (unlikely(f->tuner != 0)) + return -EINVAL; + if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + down(&btv->lock); + btv->freq = f->frequency; + bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq); + if (btv->has_matchbox && btv->radio_user) + tea5757_set_freq(btv,btv->freq); + up(&btv->lock); + return 0; + } + + default: + return -ENOIOCTLCMD; + + } + return 0; +} + +static int verify_window(const struct bttv_tvnorm *tvn, + struct v4l2_window *win, int fixup) +{ + enum v4l2_field field; + int maxw, maxh; + + if (win->w.width < 48 || win->w.height < 32) + return -EINVAL; + if (win->clipcount > 2048) + return -EINVAL; + + field = win->field; + maxw = tvn->swidth; + maxh = tvn->sheight; + + if (V4L2_FIELD_ANY == field) { + field = (win->w.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_TOP; + } + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + if (!fixup && (win->w.width > maxw || win->w.height > maxh)) + return -EINVAL; + + if (win->w.width > maxw) + win->w.width = maxw; + if (win->w.height > maxh) + win->w.height = maxh; + win->field = field; + return 0; +} + +static int setup_window(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_window *win, int fixup) +{ + struct v4l2_clip *clips = NULL; + int n,size,retval = 0; + + if (NULL == fh->ovfmt) + return -EINVAL; + retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup); + if (0 != retval) + return retval; + + /* copy clips -- luckily v4l1 + v4l2 are binary + compatible here ...*/ + n = win->clipcount; + size = sizeof(*clips)*(n+4); + clips = kmalloc(size,GFP_KERNEL); + if (NULL == clips) + return -ENOMEM; + if (n > 0) { + if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) { + kfree(clips); return -EFAULT; - if(btv->win.vidadr == 0) + } + } + /* clip against screen */ + if (NULL != btv->fbuf.base) + n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height, + &win->w, clips, n); + btcx_sort_clips(clips,n); + + /* 4-byte alignments */ + switch (fh->ovfmt->depth) { + case 8: + case 24: + btcx_align(&win->w, clips, n, 3); + break; + case 16: + btcx_align(&win->w, clips, n, 1); + break; + case 32: + /* no alignment fixups needed */ + break; + default: + BUG(); + } + + down(&fh->cap.lock); + if (fh->ov.clips) + kfree(fh->ov.clips); + fh->ov.clips = clips; + fh->ov.nclips = n; + + fh->ov.w = win->w; + fh->ov.field = win->field; + fh->ov.setup_ok = 1; + btv->init.ov.w.width = win->w.width; + btv->init.ov.w.height = win->w.height; + btv->init.ov.field = win->field; + + /* update overlay if needed */ + retval = 0; + if (check_btres(fh, RESOURCE_OVERLAY)) { + struct bttv_buffer *new; + + new = videobuf_alloc(sizeof(*new)); + bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); + retval = bttv_switch_overlay(btv,fh,new); + } + up(&fh->cap.lock); + return retval; +} + +/* ----------------------------------------------------------------------- */ + +static struct videobuf_queue* bttv_queue(struct bttv_fh *fh) +{ + struct videobuf_queue* q = NULL; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + q = &fh->cap; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + q = &fh->vbi; + break; + default: + BUG(); + } + return q; +} + +static int bttv_resource(struct bttv_fh *fh) +{ + int res = 0; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + res = RESOURCE_VIDEO; + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + res = RESOURCE_VBI; + break; + default: + BUG(); + } + return res; +} + +static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type) +{ + struct videobuf_queue *q = bttv_queue(fh); + int res = bttv_resource(fh); + + if (check_btres(fh,res)) + return -EBUSY; + if (videobuf_queue_is_busy(q)) + return -EBUSY; + fh->type = type; + return 0; +} + +static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) +{ + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format)); + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->cap.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + memset(&f->fmt.win,0,sizeof(struct v4l2_window)); + f->fmt.win.w = fh->ov.w; + f->fmt.win.field = fh->ov.field; + return 0; + case V4L2_BUF_TYPE_VBI_CAPTURE: + bttv_vbi_get_fmt(fh,f); + return 0; + default: + return -EINVAL; + } +} + +static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_format *f) +{ + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + const struct bttv_format *fmt; + enum v4l2_field field; + unsigned int maxw,maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) return -EINVAL; - if (btv->win.width==0 || btv->win.height==0) + + /* fixup format */ + maxw = bttv_tvnorms[btv->tvnorm].swidth; + maxh = bttv_tvnorms[btv->tvnorm].sheight; + field = f->fmt.pix.field; + if (V4L2_FIELD_ANY == field) + field = (f->fmt.pix.height > maxh/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + if (V4L2_FIELD_SEQ_BT == field) + field = V4L2_FIELD_SEQ_TB; + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + case V4L2_FIELD_ALTERNATE: + maxh = maxh/2; + break; + case V4L2_FIELD_INTERLACED: + break; + case V4L2_FIELD_SEQ_TB: + if (fmt->flags & FORMAT_FLAGS_PLANAR) + return -EINVAL; + break; + default: 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); + } + + /* update data for the application */ + f->fmt.pix.field = field; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return verify_window(&bttv_tvnorms[btv->tvnorm], + &f->fmt.win, 1); + case V4L2_BUF_TYPE_VBI_CAPTURE: + bttv_vbi_try_fmt(fh,f); + return 0; + default: + return -EINVAL; + } +} + +static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, + struct v4l2_format *f) +{ + int retval; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + { + const struct bttv_format *fmt; + + retval = bttv_switch_type(fh,f->type); + if (0 != retval) + return retval; + retval = bttv_try_fmt(fh,btv,f); + if (0 != retval) + return retval; + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + /* update our state informations */ + down(&fh->cap.lock); + fh->fmt = fmt; + fh->cap.field = f->fmt.pix.field; + fh->cap.last = V4L2_FIELD_NONE; + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + btv->init.fmt = fmt; + btv->init.width = f->fmt.pix.width; + btv->init.height = f->fmt.pix.height; + up(&fh->cap.lock); + + return 0; + } + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + return setup_window(fh, btv, &f->fmt.win, 1); + case V4L2_BUF_TYPE_VBI_CAPTURE: + retval = bttv_switch_type(fh,f->type); + if (0 != retval) + return retval; + if (locked_btres(fh->btv, RESOURCE_VBI)) + return -EBUSY; + bttv_vbi_try_fmt(fh,f); + bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]); + bttv_vbi_get_fmt(fh,f); + return 0; + default: + return -EINVAL; + } +} + +static int bttv_do_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, void *arg) +{ + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; + unsigned long flags; + int retval = 0; + + if (bttv_debug > 1) { + switch (_IOC_TYPE(cmd)) { + case 'v': + printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n", + btv->c.nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ? + v4l1_ioctls[_IOC_NR(cmd)] : "???"); + break; + case 'V': + printk("bttv%d: ioctl 0x%x (v4l2, %s)\n", + btv->c.nr, cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); + break; + default: + printk("bttv%d: ioctl 0x%x (???)\n", + btv->c.nr, cmd); + } + } + if (btv->errors) + bttv_reinit_bt848(btv); + +#ifdef VIDIOC_G_PRIORITY + switch (cmd) { + case VIDIOCSFREQ: + case VIDIOCSTUNER: + case VIDIOCSCHAN: + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + retval = v4l2_prio_check(&btv->prio,&fh->prio); + if (0 != retval) + return retval; + }; +#endif + switch (cmd) { + + /* *** v4l1 *** ************************************************ */ + case VIDIOCGCAP: + { + struct video_capability *cap = arg; + + memset(cap,0,sizeof(*cap)); + strcpy(cap->name,btv->video_dev->name); + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + /* vbi */ + cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT; + } else { + /* others */ + cap->type = VID_TYPE_CAPTURE| + VID_TYPE_TUNER| + VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING| + VID_TYPE_SCALES; + cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth; + cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight; + cap->minwidth = 48; + cap->minheight = 32; + } + cap->channels = bttv_tvcards[btv->c.type].video_inputs; + cap->audios = bttv_tvcards[btv->c.type].audio_inputs; + return 0; + } + + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + + memset(pic,0,sizeof(*pic)); + pic->brightness = btv->bright; + pic->contrast = btv->contrast; + pic->hue = btv->hue; + pic->colour = btv->saturation; + if (fh->fmt) { + pic->depth = fh->fmt->depth; + pic->palette = fh->fmt->palette; + } + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *pic = arg; + const struct bttv_format *fmt; + + fmt = format_by_palette(pic->palette); + if (NULL == fmt) + return -EINVAL; + down(&fh->cap.lock); + if (fmt->depth != pic->depth) { + retval = -EINVAL; + goto fh_unlock_and_return; + } + fh->ovfmt = fmt; + fh->fmt = fmt; + btv->init.ovfmt = fmt; + btv->init.fmt = fmt; + if (bigendian) { + /* dirty hack time: swap bytes for overlay if the + display adaptor is big endian (insmod option) */ + if (fmt->palette == VIDEO_PALETTE_RGB555 || + fmt->palette == VIDEO_PALETTE_RGB565 || + fmt->palette == VIDEO_PALETTE_RGB32) { + fh->ovfmt = fmt+1; + } + } + bt848_bright(btv,pic->brightness); + bt848_contrast(btv,pic->contrast); + bt848_hue(btv,pic->hue); + bt848_sat(btv,pic->colour); + up(&fh->cap.lock); + return 0; + } + + case VIDIOCGWIN: + { + struct video_window *win = arg; + + memset(win,0,sizeof(*win)); + win->x = fh->ov.w.left; + win->y = fh->ov.w.top; + win->width = fh->ov.w.width; + win->height = fh->ov.w.height; + return 0; + } + case VIDIOCSWIN: + { + struct video_window *win = arg; + struct v4l2_window w2; + + w2.field = V4L2_FIELD_ANY; + w2.w.left = win->x; + w2.w.top = win->y; + w2.w.width = win->width; + w2.w.height = win->height; + w2.clipcount = win->clipcount; + w2.clips = (struct v4l2_clip*)win->clips; + retval = setup_window(fh, btv, &w2, 0); + if (0 == retval) { + /* on v4l1 this ioctl affects the read() size too */ + fh->width = fh->ov.w.width; + fh->height = fh->ov.w.height; + btv->init.width = fh->ov.w.width; + btv->init.height = fh->ov.w.height; + } + return retval; + } + 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; + struct video_buffer *fbuf = arg; + + fbuf->base = btv->fbuf.base; + fbuf->width = btv->fbuf.fmt.width; + fbuf->height = btv->fbuf.fmt.height; + fbuf->bytesperline = btv->fbuf.fmt.bytesperline; + if (fh->ovfmt) + fbuf->depth = fh->ovfmt->depth; return 0; - } case VIDIOCSFBUF: { - struct video_buffer v; + struct video_buffer *fbuf = arg; + const struct bttv_format *fmt; + unsigned long end; + 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; + !capable(CAP_SYS_RAWIO)) + return -EPERM; + end = (unsigned long)fbuf->base + + fbuf->height * fbuf->bytesperline; + down(&fh->cap.lock); + retval = -EINVAL; + + switch (fbuf->depth) { + case 8: + fmt = format_by_palette(VIDEO_PALETTE_HI240); + break; + case 16: + fmt = format_by_palette(VIDEO_PALETTE_RGB565); + break; + case 24: + fmt = format_by_palette(VIDEO_PALETTE_RGB24); + break; + case 32: + fmt = format_by_palette(VIDEO_PALETTE_RGB32); + break; + case 15: + fbuf->depth = 16; + fmt = format_by_palette(VIDEO_PALETTE_RGB555); + break; + default: + fmt = NULL; + 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; + if (NULL == fmt) + goto fh_unlock_and_return; + + fh->ovfmt = fmt; + fh->fmt = fmt; + btv->init.ovfmt = fmt; + btv->init.fmt = fmt; + btv->fbuf.base = fbuf->base; + btv->fbuf.fmt.width = fbuf->width; + btv->fbuf.fmt.height = fbuf->height; + if (fbuf->bytesperline) + btv->fbuf.fmt.bytesperline = fbuf->bytesperline; + else + btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fbuf->depth/8; + up(&fh->cap.lock); + return 0; } - case VIDIOCKEY: + + case VIDIOCCAPTURE: + case VIDIOC_OVERLAY: { - /* Will be handled higher up .. */ - return 0; + struct bttv_buffer *new; + int *on = arg; + + if (*on) { + /* verify args */ + if (NULL == btv->fbuf.base) + return -EINVAL; + if (!fh->ov.setup_ok) { + dprintk("bttv%d: overlay: !setup_ok\n",btv->c.nr); + return -EINVAL; + } + } + + if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY)) + return -EBUSY; + + down(&fh->cap.lock); + if (*on) { + fh->ov.tvnorm = btv->tvnorm; + new = videobuf_alloc(sizeof(*new)); + bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); + } else { + new = NULL; + } + + /* switch over */ + retval = bttv_switch_overlay(btv,fh,new); + up(&fh->cap.lock); + return retval; } - case VIDIOCGFREQ: + + case VIDIOCGMBUF: { - unsigned long v=btv->win.freq; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; + struct video_mbuf *mbuf = arg; + unsigned int i; + + down(&fh->cap.lock); + retval = videobuf_mmap_setup(file,&fh->cap,gbuffers,gbufsize, + V4L2_MEMORY_MMAP); + if (retval < 0) + goto fh_unlock_and_return; + memset(mbuf,0,sizeof(*mbuf)); + mbuf->frames = gbuffers; + mbuf->size = gbuffers * gbufsize; + for (i = 0; i < gbuffers; i++) + mbuf->offsets[i] = i * gbufsize; + up(&fh->cap.lock); return 0; } - case VIDIOCSFREQ: + case VIDIOCMCAPTURE: { - 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 + struct video_mmap *vm = arg; + struct bttv_buffer *buf; + enum v4l2_field field; + + if (vm->frame >= VIDEO_MAX_FRAME) + return -EINVAL; + + down(&fh->cap.lock); + retval = -EINVAL; + buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame]; + if (NULL == buf) + goto fh_unlock_and_return; + if (0 == buf->vb.baddr) + goto fh_unlock_and_return; + if (buf->vb.state == STATE_QUEUED || + buf->vb.state == STATE_ACTIVE) + goto fh_unlock_and_return; + + field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2) + ? V4L2_FIELD_INTERLACED + : V4L2_FIELD_BOTTOM; + retval = bttv_prepare_buffer(btv,buf, + format_by_palette(vm->format), + vm->width,vm->height,field); + if (0 != retval) + goto fh_unlock_and_return; + spin_lock_irqsave(&btv->s_lock,flags); + buffer_queue(file,&buf->vb); + spin_unlock_irqrestore(&btv->s_lock,flags); + up(&fh->cap.lock); return 0; } - - case VIDIOCGAUDIO: + case VIDIOCSYNC: { - struct video_audio v; + int *frame = arg; + struct bttv_buffer *buf; + + if (*frame >= VIDEO_MAX_FRAME) + return -EINVAL; - v=btv->audio_dev; - v.flags&=~(VIDEO_AUDIO_MUTE|VIDEO_AUDIO_MUTABLE); - v.flags|=VIDEO_AUDIO_MUTABLE; - strcpy(v.name,"TV"); + down(&fh->cap.lock); + retval = -EINVAL; + buf = (struct bttv_buffer *)fh->cap.bufs[*frame]; + if (NULL == buf) + goto fh_unlock_and_return; + retval = videobuf_waiton(&buf->vb,0,1); + if (0 != retval) + goto fh_unlock_and_return; + switch (buf->vb.state) { + case STATE_ERROR: + retval = -EIO; + /* fall through */ + case STATE_DONE: + videobuf_dma_pci_sync(btv->c.pci,&buf->vb.dma); + bttv_dma_free(btv,buf); + break; + default: + retval = -EINVAL; + break; + } + up(&fh->cap.lock); + return retval; + } - v.mode = VIDEO_SOUND_MONO; - bttv_call_i2c_clients(btv,cmd,&v); + case VIDIOCGVBIFMT: + { + struct vbi_format *fmt = (void *) arg; + struct v4l2_format fmt2; - /* card specific hooks */ - if (btv->audio_hook) - btv->audio_hook(btv,&v,0); + if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) { + retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); + if (0 != retval) + return retval; + } + bttv_vbi_get_fmt(fh, &fmt2); + + memset(fmt,0,sizeof(*fmt)); + fmt->sampling_rate = fmt2.fmt.vbi.sampling_rate; + fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line; + fmt->sample_format = VIDEO_PALETTE_RAW; + fmt->start[0] = fmt2.fmt.vbi.start[0]; + fmt->count[0] = fmt2.fmt.vbi.count[0]; + fmt->start[1] = fmt2.fmt.vbi.start[1]; + fmt->count[1] = fmt2.fmt.vbi.count[1]; + if (fmt2.fmt.vbi.flags & VBI_UNSYNC) + fmt->flags |= V4L2_VBI_UNSYNC; + if (fmt2.fmt.vbi.flags & VBI_INTERLACED) + fmt->flags |= V4L2_VBI_INTERLACED; + return 0; + } + case VIDIOCSVBIFMT: + { + struct vbi_format *fmt = (void *) arg; + struct v4l2_format fmt2; + + retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); + if (0 != retval) + return retval; + bttv_vbi_get_fmt(fh, &fmt2); + + if (fmt->sampling_rate != fmt2.fmt.vbi.sampling_rate || + fmt->samples_per_line != fmt2.fmt.vbi.samples_per_line || + fmt->sample_format != VIDEO_PALETTE_RAW || + fmt->start[0] != fmt2.fmt.vbi.start[0] || + fmt->start[1] != fmt2.fmt.vbi.start[1] || + fmt->count[0] != fmt->count[1] || + fmt->count[0] < 1 || + fmt->count[0] > 32 /* VBI_MAXLINES */) + return -EINVAL; - if(copy_to_user(arg,&v,sizeof(v))) - return -EFAULT; + bttv_vbi_setlines(fh,btv,fmt->count[0]); return 0; } + + case BTTV_VERSION: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGCHAN: + case VIDIOCSCHAN: + case VIDIOCGAUDIO: case VIDIOCSAUDIO: + return bttv_common_ioctls(btv,cmd,arg); + + /* *** v4l2 *** ************************************************ */ + case VIDIOC_QUERYCAP: { - struct video_audio v; - unsigned int n; + struct v4l2_capability *cap = arg; - if(copy_from_user(&v,arg, sizeof(v))) - return -EFAULT; - n = v.audio; - if(n >= bttv_tvcards[btv->type].audio_inputs) + if (0 == v4l2) return -EINVAL; - down(&btv->lock); - if(v.flags&VIDEO_AUDIO_MUTE) - audio(btv, AUDIO_MUTE); - /* bt848_muxsel(btv,v.audio); */ - if(!(v.flags&VIDEO_AUDIO_MUTE)) - audio(btv, AUDIO_UNMUTE); - - bttv_call_i2c_clients(btv,cmd,&v); - - /* card specific hooks */ - if (btv->audio_hook) - btv->audio_hook(btv,&v,1); - - btv->audio_dev=v; - up(&btv->lock); + strcpy(cap->driver,"bttv"); + strlcpy(cap->card,btv->video_dev->name,sizeof(cap->card)); + sprintf(cap->bus_info,"PCI:%s",pci_name(btv->c.pci)); + cap->version = BTTV_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_VBI_CAPTURE | + V4L2_CAP_TUNER | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; return 0; } - case VIDIOCSYNC: + case VIDIOC_ENUM_FMT: { - DECLARE_WAITQUEUE(wait, current); + struct v4l2_fmtdesc *f = arg; + enum v4l2_buf_type type; unsigned int i; + int index; - if(copy_from_user((void *)&i,arg,sizeof(int))) - return -EFAULT; - if (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; + type = f->type; + if (V4L2_BUF_TYPE_VBI_CAPTURE == type) { + /* vbi */ + index = f->index; + if (0 != index) + return -EINVAL; + memset(f,0,sizeof(*f)); + f->index = index; + f->type = type; + f->pixelformat = V4L2_PIX_FMT_GREY; + strcpy(f->description,"vbi data"); + return 0; + } + + /* video capture + overlay */ + index = -1; + for (i = 0; i < BTTV_FORMATS; i++) { + if (bttv_formats[i].fourcc != -1) + index++; + if ((unsigned int)index == f->index) + break; } - if (btv->needs_restart) { - down(&btv->lock); - bt848_restart(btv); - up(&btv->lock); + if (BTTV_FORMATS == i) + return -EINVAL; + + switch (f->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + break; + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; + break; + default: + return -EINVAL; } - return ret; + memset(f,0,sizeof(*f)); + f->index = index; + f->type = type; + f->pixelformat = bttv_formats[i].fourcc; + strlcpy(f->description,bttv_formats[i].name,sizeof(f->description)); + return 0; } - 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 VIDIOC_TRY_FMT: + { + struct v4l2_format *f = arg; + return bttv_try_fmt(fh,btv,f); } - - case VIDIOCMCAPTURE: + case VIDIOC_G_FMT: { - 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; + struct v4l2_format *f = arg; + return bttv_g_fmt(fh,f); } - - case VIDIOCGMBUF: + case VIDIOC_S_FMT: { - struct video_mbuf vm; - unsigned int i; - - 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; + struct v4l2_format *f = arg; + return bttv_s_fmt(fh,btv,f); } - - case VIDIOCGUNIT: + + case VIDIOC_G_FBUF: { - 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; + struct v4l2_framebuffer *fb = arg; + + *fb = btv->fbuf; + fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + if (fh->ovfmt) + fb->fmt.pixelformat = fh->ovfmt->fourcc; return 0; } + case VIDIOC_S_FBUF: + { + struct v4l2_framebuffer *fb = arg; + const struct bttv_format *fmt; + + if(!capable(CAP_SYS_ADMIN) && + !capable(CAP_SYS_RAWIO)) + return -EPERM; + + /* check args */ + fmt = format_by_fourcc(fb->fmt.pixelformat); + if (NULL == fmt) + return -EINVAL; + if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) + return -EINVAL; + + down(&fh->cap.lock); + retval = -EINVAL; + if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { + if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) + goto fh_unlock_and_return; + if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight) + goto fh_unlock_and_return; + } + + /* ok, accept it */ + btv->fbuf.base = fb->base; + btv->fbuf.fmt.width = fb->fmt.width; + btv->fbuf.fmt.height = fb->fmt.height; + if (0 != fb->fmt.bytesperline) + btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline; + else + btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8; + + retval = 0; + fh->ovfmt = fmt; + btv->init.ovfmt = fmt; + if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { + fh->ov.w.left = 0; + fh->ov.w.top = 0; + fh->ov.w.width = fb->fmt.width; + fh->ov.w.height = fb->fmt.height; + btv->init.ov.w.width = fb->fmt.width; + btv->init.ov.w.height = fb->fmt.height; + if (fh->ov.clips) + kfree(fh->ov.clips); + fh->ov.clips = NULL; + fh->ov.nclips = 0; + + if (check_btres(fh, RESOURCE_OVERLAY)) { + struct bttv_buffer *new; - case BTTV_BURST_ON: + new = videobuf_alloc(sizeof(*new)); + bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new); + retval = bttv_switch_overlay(btv,fh,new); + } + } + up(&fh->cap.lock); + return retval; + } + + case VIDIOC_REQBUFS: + return videobuf_reqbufs(file,bttv_queue(fh),arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(bttv_queue(fh),arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(file,bttv_queue(fh),arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(file,bttv_queue(fh),arg); + + case VIDIOC_STREAMON: { - burst(1); + int res = bttv_resource(fh); + + if (!check_alloc_btres(btv,fh,res)) + return -EBUSY; + return videobuf_streamon(file,bttv_queue(fh)); + } + case VIDIOC_STREAMOFF: + { + int res = bttv_resource(fh); + + retval = videobuf_streamoff(file,bttv_queue(fh)); + if (retval < 0) + return retval; + free_btres(btv,fh,res); return 0; } - case BTTV_BURST_OFF: + case VIDIOC_QUERYCTRL: { - burst(0); + struct v4l2_queryctrl *c = arg; + int i; + + if ((c->id < V4L2_CID_BASE || + c->id >= V4L2_CID_LASTP1) && + (c->id < V4L2_CID_PRIVATE_BASE || + c->id >= V4L2_CID_PRIVATE_LASTP1)) + return -EINVAL; + for (i = 0; i < BTTV_CTLS; i++) + if (bttv_ctls[i].id == c->id) + break; + if (i == BTTV_CTLS) { + *c = no_ctl; + return 0; + } + *c = bttv_ctls[i]; + if (i >= 4 && i <= 8) { + struct video_audio va; + memset(&va,0,sizeof(va)); + bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); + if (btv->audio_hook) + btv->audio_hook(btv,&va,0); + switch (bttv_ctls[i].id) { + case V4L2_CID_AUDIO_VOLUME: + if (!(va.flags & VIDEO_AUDIO_VOLUME)) + *c = no_ctl; + break; + case V4L2_CID_AUDIO_BALANCE: + if (!(va.flags & VIDEO_AUDIO_BALANCE)) + *c = no_ctl; + break; + case V4L2_CID_AUDIO_BASS: + if (!(va.flags & VIDEO_AUDIO_BASS)) + *c = no_ctl; + break; + case V4L2_CID_AUDIO_TREBLE: + if (!(va.flags & VIDEO_AUDIO_TREBLE)) + *c = no_ctl; + break; + } + } + return 0; + } + case VIDIOC_G_CTRL: + return get_control(btv,arg); + case VIDIOC_S_CTRL: + return set_control(btv,arg); + case VIDIOC_G_PARM: + { + struct v4l2_streamparm *parm = arg; + struct v4l2_standard s; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(parm,0,sizeof(*parm)); + v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, + bttv_tvnorms[btv->tvnorm].name); + parm->parm.capture.timeperframe = s.frameperiod; return 0; } - case BTTV_VERSION: +#ifdef VIDIOC_G_PRIORITY + case VIDIOC_G_PRIORITY: { - return BTTV_VERSION_CODE; + enum v4l2_priority *p = arg; + + *p = v4l2_prio_max(&btv->prio); + return 0; } - - case BTTV_PICNR: + case VIDIOC_S_PRIORITY: { - /* return picture;*/ - return 0; + enum v4l2_priority *prio = arg; + + return v4l2_prio_change(&btv->prio, &fh->prio, *prio); } +#endif + + + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_ENUMINPUT: + case VIDIOC_G_INPUT: + case VIDIOC_S_INPUT: + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + return bttv_common_ioctls(btv,cmd,arg); 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 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(start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - size-=PAGE_SIZE; - } - return 0; + fh_unlock_and_return: + up(&fh->cap.lock); + return retval; } -static int bttv_mmap(struct video_device *dev, const char *adr, unsigned long size) +static int bttv_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { - struct bttv *btv=(struct bttv *)dev; - int r; + struct bttv_fh *fh = file->private_data; - down(&btv->lock); - r=do_bttv_mmap(btv, adr, size); - up(&btv->lock); - return r; + switch (cmd) { + case BTTV_VBISIZE: + bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); + return fh->lines * 2 * 2048; + default: + return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl); + } } +static ssize_t bttv_read(struct file *file, char *data, + size_t count, loff_t *ppos) +{ + struct bttv_fh *fh = file->private_data; + int retval = 0; -static struct video_device bttv_template= -{ - .owner = THIS_MODULE, - .name = "bttv video", - .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, -}; + if (fh->btv->errors) + bttv_reinit_bt848(fh->btv); + dprintk("bttv%d: read count=%d type=%s\n", + fh->btv->c.nr,(int)count,v4l2_type_names[fh->type]); + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (locked_btres(fh->btv,RESOURCE_VIDEO)) + return -EBUSY; + retval = videobuf_read_one(file, &fh->cap, data, count, ppos); + break; + case V4L2_BUF_TYPE_VBI_CAPTURE: + if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) + return -EBUSY; + retval = videobuf_read_stream(file, &fh->vbi, data, count, ppos, 1); + break; + default: + BUG(); + } + return retval; +} -static long vbi_read(struct video_device *v, char *buf, unsigned long count, - int nonblock) +static unsigned int bttv_poll(struct file *file, poll_table *wait) { - struct bttv *btv=(struct bttv *)(v-2); - unsigned long todo; - unsigned int q; - 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; + struct bttv_fh *fh = file->private_data; + struct bttv_buffer *buf; + enum v4l2_field field; - 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; + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) + return -EBUSY; + return videobuf_poll_stream(file, &fh->vbi, wait); + } + + if (check_btres(fh,RESOURCE_VIDEO)) { + /* streaming capture */ + if (list_empty(&fh->cap.stream)) + return POLLERR; + buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); + } else { + /* read() capture */ + down(&fh->cap.lock); + if (NULL == fh->cap.read_buf) { + /* need to capture a new frame */ + if (locked_btres(fh->btv,RESOURCE_VIDEO)) { + up(&fh->cap.lock); + return POLLERR; + } + fh->cap.read_buf = videobuf_alloc(fh->cap.msize); + if (NULL == fh->cap.read_buf) { + up(&fh->cap.lock); + return POLLERR; } - schedule(); - if(signal_pending(current)) - { - remove_wait_queue(&btv->vbiq, &wait); - current->state = TASK_RUNNING; - if(todo==count) - return -EINTR; - else - return count-todo; + fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; + field = videobuf_next_field(&fh->cap); + if (0 != fh->cap.ops->buf_prepare(file,fh->cap.read_buf,field)) { + up(&fh->cap.lock); + return POLLERR; } + fh->cap.ops->buf_queue(file,fh->cap.read_buf); + fh->cap.read_off = 0; } - remove_wait_queue(&btv->vbiq, &wait); - current->state = TASK_RUNNING; + up(&fh->cap.lock); + buf = (struct bttv_buffer*)fh->cap.read_buf; } - if (todo) - { - if(copy_to_user((void *) buf, (void *) btv->vbibuf+btv->vbip, todo)) - return -EFAULT; - btv->vbip+=todo; - } - return count; + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == STATE_DONE || + buf->vb.state == STATE_ERROR) + return POLLIN|POLLRDNORM; + return 0; } -static unsigned int vbi_poll(struct video_device *dev, struct file *file, - poll_table *wait) +static int bttv_open(struct inode *inode, struct file *file) { - struct bttv *btv=(struct bttv *)(dev-2); - unsigned int mask = 0; + int minor = iminor(inode); + struct bttv *btv = NULL; + struct bttv_fh *fh; + enum v4l2_buf_type type = 0; + unsigned int i; + + dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor); + + for (i = 0; i < bttv_num; i++) { + if (bttvs[i].video_dev && + bttvs[i].video_dev->minor == minor) { + btv = &bttvs[i]; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + break; + } + if (bttvs[i].vbi_dev && + bttvs[i].vbi_dev->minor == minor) { + btv = &bttvs[i]; + type = V4L2_BUF_TYPE_VBI_CAPTURE; + break; + } + } + if (NULL == btv) + return -ENODEV; - poll_wait(file, &btv->vbiq, wait); + dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n", + btv->c.nr,v4l2_type_names[type]); - if (btv->vbip < VBIBUF_SIZE) - mask |= (POLLIN | POLLRDNORM); + /* allocate per filehandle data */ + fh = kmalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + file->private_data = fh; + *fh = btv->init; + fh->type = type; + fh->ov.setup_ok = 0; +#ifdef VIDIOC_G_PRIORITY + v4l2_prio_open(&btv->prio,&fh->prio); +#endif - return mask; + videobuf_queue_init(&fh->cap, &bttv_video_qops, + btv->c.pci, &btv->s_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct bttv_buffer)); + videobuf_queue_init(&fh->vbi, &bttv_vbi_qops, + btv->c.pci, &btv->s_lock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct bttv_buffer)); + i2c_vidiocschan(btv); + + btv->users++; + if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) + bttv_vbi_setlines(fh,btv,16); + bttv_field_count(btv); + return 0; } -static int vbi_open(struct video_device *dev, int flags) +static int bttv_release(struct inode *inode, struct file *file) { - struct bttv *btv=(struct bttv *)(dev-2); - unsigned long irq_flags; + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; - 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); + /* turn off overlay */ + if (check_btres(fh, RESOURCE_OVERLAY)) + bttv_switch_overlay(btv,fh,NULL); + + /* stop video capture */ + if (check_btres(fh, RESOURCE_VIDEO)) { + videobuf_streamoff(file,&fh->cap); + free_btres(btv,fh,RESOURCE_VIDEO); + } + if (fh->cap.read_buf) { + buffer_release(file,fh->cap.read_buf); + kfree(fh->cap.read_buf); + } - return 0; -} + /* stop vbi capture */ + if (check_btres(fh, RESOURCE_VBI)) { + if (fh->vbi.streaming) + videobuf_streamoff(file,&fh->vbi); + if (fh->vbi.reading) + videobuf_read_stop(file,&fh->vbi); + free_btres(btv,fh,RESOURCE_VBI); + } -static void vbi_close(struct video_device *dev) -{ - struct bttv *btv=(struct bttv *)(dev-2); - unsigned long irq_flags; +#ifdef VIDIOC_G_PRIORITY + v4l2_prio_close(&btv->prio,&fh->prio); +#endif + file->private_data = NULL; + kfree(fh); - 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); + btv->users--; + bttv_field_count(btv); + return 0; } -static int vbi_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +static int +bttv_mmap(struct file *file, struct vm_area_struct *vma) { - struct bttv *btv=(struct bttv *)(dev-2); + struct bttv_fh *fh = file->private_data; - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability b; - strcpy(b.name,btv->vbi_dev.name); - b.type = ((bttv_tvcards[btv->type].tuner != UNSET) ? 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; - } -} + dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n", + fh->btv->c.nr, v4l2_type_names[fh->type], + vma->vm_start, vma->vm_end - vma->vm_start); + return videobuf_mmap_mapper(vma,bttv_queue(fh)); +} + +static struct file_operations bttv_fops = +{ + .owner = THIS_MODULE, + .open = bttv_open, + .release = bttv_release, + .ioctl = bttv_ioctl, + .llseek = no_llseek, + .read = bttv_read, + .mmap = bttv_mmap, + .poll = bttv_poll, +}; -static struct video_device vbi_template= +static struct video_device bttv_video_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, + .name = "UNSET", + .type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY| + VID_TYPE_CLIPPING|VID_TYPE_SCALES, + .hardware = VID_HARDWARE_BT848, + .fops = &bttv_fops, + .minor = -1, }; - -static int radio_open(struct video_device *dev, int flags) +struct video_device bttv_vbi_template = { - struct bttv *btv = (struct bttv *)(dev-1); + .name = "bt848/878 vbi", + .type = VID_TYPE_TUNER|VID_TYPE_TELETEXT, + .hardware = VID_HARDWARE_BT848, + .fops = &bttv_fops, + .minor = -1, +}; - down(&btv->lock); - if (btv->user) - goto busy_unlock; - btv->user++; - - btv->radio = 1; - bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); - bt848_muxsel(btv,0); - up(&btv->lock); +/* ----------------------------------------------------------------------- */ +/* radio interface */ - return 0; +static int radio_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct bttv *btv = NULL; + unsigned int i; - busy_unlock: - up(&btv->lock); - return -EBUSY; -} + dprintk("bttv: open minor=%d\n",minor); -static void radio_close(struct video_device *dev) -{ - struct bttv *btv=(struct bttv *)(dev-1); + for (i = 0; i < bttv_num; i++) { + if (bttvs[i].radio_dev->minor == minor) { + btv = &bttvs[i]; + break; + } + } + if (NULL == btv) + return -ENODEV; + dprintk("bttv%d: open called (radio)\n",btv->c.nr); down(&btv->lock); - btv->user--; - btv->radio = 0; + if (btv->radio_user) { + up(&btv->lock); + return -EBUSY; + } + btv->radio_user++; + file->private_data = btv; + + i2c_vidiocschan(btv); + bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); + audio_mux(btv,AUDIO_RADIO); + up(&btv->lock); + return 0; } -static long radio_read(struct video_device *v, char *buf, unsigned long count, int nonblock) +static int radio_release(struct inode *inode, struct file *file) { - return -EINVAL; + struct bttv *btv = file->private_data; + + btv->radio_user--; + return 0; } -static int radio_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +static int radio_do_ioctl(struct inode *inode, struct file *file, + 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 bttv *btv = file->private_data; + + switch (cmd) { + case VIDIOCGCAP: { - 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; + struct video_capability *cap = arg; + + memset(cap,0,sizeof(*cap)); + strcpy(cap->name,btv->radio_dev->name); + cap->type = VID_TYPE_TUNER; + cap->channels = 1; + cap->audios = 1; + 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 ??? */ + + case VIDIOCGTUNER: + { + struct video_tuner *v = arg; + + if(v->tuner) + return -EINVAL; + memset(v,0,sizeof(*v)); + 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); + bttv_call_i2c_clients(btv,cmd,v); + return 0; + } + case VIDIOCSTUNER: + /* nothing to do */ return 0; - } - case VIDIOCGFREQ: - case VIDIOCSFREQ: + + case BTTV_VERSION: + case VIDIOCGFREQ: + case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: - bttv_ioctl((struct video_device *)btv,cmd,arg); - break; + return bttv_common_ioctls(btv,cmd,arg); + default: return -ENOIOCTLCMD; } return 0; } -static struct video_device radio_template= +static int radio_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) { - .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, + return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); +} + +static struct file_operations radio_fops = +{ + .owner = THIS_MODULE, + .open = radio_open, + .release = radio_release, + .ioctl = radio_ioctl, + .llseek = no_llseek, +}; + +static struct video_device radio_template = +{ + .name = "bt848/878 radio", + .type = VID_TYPE_TUNER, + .hardware = VID_HARDWARE_BT848, + .fops = &radio_fops, + .minor = -1, }; +/* ----------------------------------------------------------------------- */ +/* irq handler */ + +static char *irq_name[] = { + "FMTCHG", // format change detected (525 vs. 625) + "VSYNC", // vertical sync (new field) + "HSYNC", // horizontal sync + "OFLOW", // chroma/luma AGC overflow + "HLOCK", // horizontal lock changed + "VPRES", // video presence changed + "6", "7", + "I2CDONE", // hw irc operation finished + "GPINT", // gpio port triggered irq + "10", + "RISCI", // risc instruction triggered irq + "FBUS", // pixel data fifo dropped data (high pci bus latencies) + "FTRGT", // pixel data fifo overrun + "FDSR", // fifo data stream resyncronisation + "PPERR", // parity error (data transfer) + "RIPERR", // parity error (read risc instructions) + "PABORT", // pci abort + "OCERR", // risc instruction error + "SCERR", // syncronisation error +}; -static void bt848_set_risc_jmps(struct bttv *btv, int flags) +static void bttv_print_irqbits(u32 print, u32 mark) { - if (-1 == flags) { - /* defaults */ - flags = 0; - if (btv->scr_on) - flags |= 0x03; - if (btv->vbi_on) - flags |= 0x0c; - if (bttv_tvcards[btv->type].muxsel[btv->channel] < 0) - flags |= 0x0c; -#if 0 - /* Hmm ... */ - if ((0 != btv->risc_cap_even) || - (0 != btv->risc_cap_odd)) - flags |= 0x0c; -#endif + unsigned int i; + + printk("bits:"); + for (i = 0; i < ARRAY_SIZE(irq_name); i++) { + if (print & (1 << i)) + printk(" %s",irq_name[i]); + if (mark & (1 << i)) + printk("*"); } +} - 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)); +static void bttv_print_riscaddr(struct bttv *btv) +{ + printk(" main: %08Lx\n", + (unsigned long long)btv->main.dma); + printk(" vbi : o=%08Lx e=%08Lx\n", + btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, + btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0); + printk(" cap : o=%08Lx e=%08Lx\n", + btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, + btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); + printk(" scr : o=%08Lx e=%08Lx\n", + btv->screen ? (unsigned long long)btv->screen->top.dma : 0, + btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0); +} + +static void bttv_irq_debug_low_latency(struct bttv *btv, u32 rc) +{ + printk("bttv%d: irq: skipped frame [main=%lx,o_vbi=%lx,o_field=%lx,rc=%lx]\n", + btv->c.nr, + (unsigned long)btv->main.dma, + (unsigned long)btv->main.cpu[RISC_SLOT_O_VBI+1], + (unsigned long)btv->main.cpu[RISC_SLOT_O_FIELD+1], + (unsigned long)rc); + + if (0 == (btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC)) { + printk("bttv%d: Oh, there (temporarely?) is no input signal. " + "Ok, then this is harmless, don't worry ;)\n", + btv->c.nr); + return; } - - - /* 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)); + printk("bttv%d: Uhm. Looks like we have unusual high IRQ latencies.\n", + btv->c.nr); + printk("bttv%d: Lets try to catch the culpit red-handed ...\n", + btv->c.nr); + dump_stack(); +} + +static int +bttv_irq_next_video(struct bttv *btv, struct bttv_buffer_set *set) +{ + struct bttv_buffer *item; + + memset(set,0,sizeof(*set)); + + /* capture request ? */ + if (!list_empty(&btv->capture)) { + set->irqflags = 1; + item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); + if (V4L2_FIELD_HAS_TOP(item->vb.field)) + set->top = item; + if (V4L2_FIELD_HAS_BOTTOM(item->vb.field)) + set->bottom = item; + + /* capture request for other field ? */ + if (!V4L2_FIELD_HAS_BOTH(item->vb.field) && + (item->vb.queue.next != &btv->capture)) { + item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue); + if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) { + if (NULL == set->top && + V4L2_FIELD_TOP == item->vb.field) { + set->top = item; + } + if (NULL == set->bottom && + V4L2_FIELD_BOTTOM == item->vb.field) { + set->bottom = item; + } + if (NULL != set->top && NULL != set->bottom) + set->topirq = 2; + } + } } - 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); + /* screen overlay ? */ + if (NULL != btv->screen) { + if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) { + if (NULL == set->top && NULL == set->bottom) { + set->top = btv->screen; + set->bottom = btv->screen; + } + } else { + if (V4L2_FIELD_TOP == btv->screen->vb.field && + NULL == set->top) { + set->top = btv->screen; + } + if (V4L2_FIELD_BOTTOM == btv->screen->vb.field && + NULL == set->bottom) { + set->bottom = btv->screen; + } + } } - 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); + dprintk("bttv%d: next set: top=%p bottom=%p [screen=%p,irq=%d,%d]\n", + btv->c.nr,set->top, set->bottom, + btv->screen,set->irqflags,set->topirq); + return 0; } -static int __devinit init_video_dev(struct bttv *btv) -{ - audio(btv, AUDIO_MUTE); - - if (video_register_device(&btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) - return -1; - printk(KERN_INFO "bttv%d: registered device video%d\n", - btv->nr,btv->video_dev.minor & 0x1f); - if (video_register_device(&btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) { - video_unregister_device(&btv->video_dev); - return -1; - } - printk(KERN_INFO "bttv%d: registered device vbi%d\n", - btv->nr,btv->vbi_dev.minor & 0x1f); - if (btv->has_radio) { - if(video_register_device(&btv->radio_dev, VFL_TYPE_RADIO, radio_nr)<0) { - video_unregister_device(&btv->vbi_dev); - video_unregister_device(&btv->video_dev); - return -1; +static void +bttv_irq_wakeup_video(struct bttv *btv, struct bttv_buffer_set *wakeup, + struct bttv_buffer_set *curr, unsigned int state) +{ + struct timeval ts; + + do_gettimeofday(&ts); + + if (wakeup->top == wakeup->bottom) { + if (NULL != wakeup->top && curr->top != wakeup->top) { + if (irq_debug > 1) + printk("bttv%d: wakeup: both=%p\n",btv->c.nr,wakeup->top); + wakeup->top->vb.ts = ts; + wakeup->top->vb.field_count = btv->field_count; + wakeup->top->vb.state = state; + wake_up(&wakeup->top->vb.done); + } + } else { + if (NULL != wakeup->top && curr->top != wakeup->top) { + if (irq_debug > 1) + printk("bttv%d: wakeup: top=%p\n",btv->c.nr,wakeup->top); + wakeup->top->vb.ts = ts; + wakeup->top->vb.field_count = btv->field_count; + wakeup->top->vb.state = state; + wake_up(&wakeup->top->vb.done); + } + if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) { + if (irq_debug > 1) + printk("bttv%d: wakeup: bottom=%p\n",btv->c.nr,wakeup->bottom); + wakeup->bottom->vb.ts = ts; + wakeup->bottom->vb.field_count = btv->field_count; + wakeup->bottom->vb.state = state; + wake_up(&wakeup->bottom->vb.done); } - printk(KERN_INFO "bttv%d: registered device radio%d\n", - btv->nr,btv->radio_dev.minor & 0x1f); } - return 1; } -static int __devinit init_bt848(struct bttv *btv) +static void +bttv_irq_wakeup_vbi(struct bttv *btv, struct bttv_buffer *wakeup, + unsigned int state) { - int val; - unsigned int j; - unsigned long irq_flags; + struct timeval ts; - 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"); - bttv_gpio_tracking(btv,"init #1"); - } - - /* reset the bt848 */ - btwrite(0, BT848_SRESET); - - /* 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; + if (NULL == wakeup) + return; - btv->errors=0; - btv->needs_restart=0; - btv->has_radio=radio[btv->nr]; + do_gettimeofday(&ts); + wakeup->vb.ts = ts; + wakeup->vb.field_count = btv->field_count; + wakeup->vb.state = state; + wake_up(&wakeup->vb.done); +} - 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; - } +static void bttv_irq_timeout(unsigned long data) +{ + struct bttv *btv = (struct bttv *)data; + struct bttv_buffer_set old,new; + struct bttv_buffer *ovbi; + struct bttv_buffer *item; + unsigned long flags; - 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); + if (bttv_verbose) { + printk(KERN_INFO "bttv%d: timeout: drop=%d irq=%d/%d, risc=%08x, ", + btv->c.nr, btv->framedrop, btv->irq_me, btv->irq_total, + btread(BT848_RISC_COUNT)); + bttv_print_irqbits(btread(BT848_INT_STAT),0); + printk("\n"); + } + + spin_lock_irqsave(&btv->s_lock,flags); + + /* deactivate stuff */ + memset(&new,0,sizeof(new)); + old = btv->curr; + ovbi = btv->cvbi; + btv->curr = new; + btv->cvbi = NULL; + bttv_buffer_activate_video(btv, &new); + bttv_buffer_activate_vbi(btv, NULL); + bttv_set_dma(btv, 0, 0); + + /* wake up */ + bttv_irq_wakeup_video(btv, &old, &new, STATE_ERROR); + bttv_irq_wakeup_vbi(btv, ovbi, STATE_ERROR); + + /* cancel all outstanding capture / vbi requests */ + while (!list_empty(&btv->capture)) { + item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); + list_del(&item->vb.queue); + item->vb.state = STATE_ERROR; + wake_up(&item->vb.done); + } + while (!list_empty(&btv->vcapture)) { + item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); + list_del(&item->vb.queue); + item->vb.state = STATE_ERROR; + wake_up(&item->vb.done); + } + + btv->errors++; + spin_unlock_irqrestore(&btv->s_lock,flags); +} - btwrite(0x20, BT848_E_VSCALE_HI); - btwrite(0x20, BT848_O_VSCALE_HI); - btwrite(BT848_ADC_RESERVED | (adc_crush ? BT848_ADC_CRUSH : 0), - BT848_ADC); +static void +bttv_irq_wakeup_top(struct bttv *btv) +{ + struct bttv_buffer *wakeup = btv->curr.top; - 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); - } + if (NULL == wakeup) + return; - btv->picture.colour = 254<<7; - btv->picture.brightness = 128<<8; - btv->picture.hue = 128<<8; - btv->picture.contrast = 0xd8<<7; - btv->picture.palette = VIDEO_PALETTE_RGB24; + spin_lock(&btv->s_lock); + btv->curr.topirq = 0; + btv->curr.top = NULL; + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); + + do_gettimeofday(&wakeup->vb.ts); + wakeup->vb.field_count = btv->field_count; + wakeup->vb.state = STATE_DONE; + wake_up(&wakeup->vb.done); + spin_unlock(&btv->s_lock); +} - val = chroma_agc ? BT848_SCLOOP_CAGC : 0; - btwrite(val, BT848_E_SCLOOP); - btwrite(val, BT848_O_SCLOOP); +static inline int is_active(struct btcx_riscmem *risc, u32 rc) +{ + if (rc < risc->dma) + return 0; + if (rc > risc->dma + risc->size) + return 0; + return 1; +} - /* 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) | - (gpint ? BT848_INT_GPINT : 0) | - BT848_INT_SCERR| - BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| - BT848_INT_FMTCHG|BT848_INT_HLOCK, - BT848_INT_MASK); - - 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); +static void +bttv_irq_switch_video(struct bttv *btv) +{ + struct bttv_buffer_set new; + struct bttv_buffer_set old; + dma_addr_t rc; - /* needs to be done before i2c is registered */ - bttv_init_card1(btv); + spin_lock(&btv->s_lock); - /* register i2c */ - btv->tuner_type = UNSET; - btv->pinnacle_id = UNSET; - init_bttv_i2c(btv); + /* new buffer set */ + bttv_irq_next_video(btv, &new); + rc = btread(BT848_RISC_COUNT); + if ((btv->curr.top && is_active(&btv->curr.top->top, rc)) || + (btv->curr.bottom && is_active(&btv->curr.bottom->bottom, rc))) { + btv->framedrop++; + if (debug_latency) + bttv_irq_debug_low_latency(btv, rc); + spin_unlock(&btv->s_lock); + return; + } + + /* switch over */ + old = btv->curr; + btv->curr = new; + bttv_buffer_activate_video(btv, &new); + bttv_set_dma(btv, 0, new.irqflags); + + /* switch input */ + if (UNSET != btv->new_input) { + video_mux(btv,btv->new_input); + btv->new_input = UNSET; + } - /* some card-specific stuff (needs working i2c) */ - bttv_init_card2(btv); + /* wake up finished buffers */ + bttv_irq_wakeup_video(btv, &old, &new, STATE_DONE); + spin_unlock(&btv->s_lock); +} - bt848_muxsel(btv, 1); - bt848_set_winsize(btv); +static void +bttv_irq_switch_vbi(struct bttv *btv) +{ + struct bttv_buffer *new = NULL; + struct bttv_buffer *old; + u32 rc; - /* - * Now add the template and register the device unit. - */ - init_video_dev(btv); + spin_lock(&btv->s_lock); - return 0; + if (!list_empty(&btv->vcapture)) + new = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); + old = btv->cvbi; + + rc = btread(BT848_RISC_COUNT); + if (NULL != old && (is_active(&old->top, rc) || + is_active(&old->bottom, rc))) { + btv->framedrop++; + if (debug_latency) + bttv_irq_debug_low_latency(btv, rc); + spin_unlock(&btv->s_lock); + return; + } + + /* switch */ + btv->cvbi = new; + bttv_buffer_activate_vbi(btv, new); + bttv_set_dma(btv, 0, btv->curr.irqflags); + + bttv_irq_wakeup_vbi(btv, old, STATE_DONE); + spin_unlock(&btv->s_lock); } -/* ----------------------------------------------------------------------- */ - -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) +static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs) { u32 stat,astat; u32 dstat; int count; struct bttv *btv; + int handled = 0; btv=(struct bttv *)dev_id; count=0; - while (1) - { + while (1) { /* get/clear interrupt status bits */ stat=btread(BT848_INT_STAT); astat=stat&btread(BT848_INT_MASK); if (!astat) - return; + break; + handled = 1; btwrite(stat,BT848_INT_STAT); /* get device status bits */ dstat=btread(BT848_DSTATUS); if (irq_debug) { - unsigned int i; - printk(KERN_DEBUG "bttv%d: irq loop=%d risc=%x, bits:", - btv->nr, count, stat>>28); - for (i = 0; i < ARRAY_SIZE(irq_name); i++) { - if (stat & (1 << i)) - printk(" %s",irq_name[i]); - if (astat & (1 << i)) - printk("*"); - } + printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d " + "riscs=%x, riscc=%08x, ", + btv->c.nr, count, btv->field_count, + stat>>28, btread(BT848_RISC_COUNT)); + bttv_print_irqbits(stat,astat); if (stat & BT848_INT_HLOCK) printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC) ? "yes" : "no"); @@ -2829,324 +3527,442 @@ printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES) ? "yes" : "no"); if (stat & BT848_INT_FMTCHG) - printk(" NUML => %s", (dstat & BT848_DSTATUS_PRES) + printk(" NUML => %s", (dstat & BT848_DSTATUS_NUML) ? "625" : "525"); printk("\n"); } - if (astat&BT848_INT_GPINT) - wake_up_interruptible(&btv->gpioq); - if (astat&BT848_INT_VSYNC) - btv->field++; + btv->field_count++; - 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); - 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_GPINT) { + wake_up(&btv->gpioq); } - 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); - btwrite(BT848_COLOR_CTL_GAMMA, - BT848_COLOR_CTL); - } else { - btv->risc_cap_odd = 0; - btv->risc_cap_even = 0; - bt848_set_risc_jmps(btv,-1); - bt848_set_geo(btv); - btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA, - BT848_COLOR_CTL); - } - 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); - btwrite(BT848_COLOR_CTL_GAMMA, - BT848_COLOR_CTL); - spin_unlock(&btv->s_lock); - } + if (astat & BT848_INT_I2CDONE) { + btv->i2c_done = stat; + wake_up(&btv->i2c_queue); } - if (automute && (astat&BT848_INT_HLOCK)) { - if ((dstat&BT848_DSTATUS_HLOC) || (btv->radio)) - audio(btv, AUDIO_ON); - else - audio(btv, AUDIO_OFF); + if ((astat & BT848_INT_RISCI) && (stat & (4<<28))) + bttv_irq_switch_vbi(btv); + + if ((astat & BT848_INT_RISCI) && (stat & (2<<28))) + bttv_irq_wakeup_top(btv); + + if ((astat & BT848_INT_RISCI) && (stat & (1<<28))) + bttv_irq_switch_video(btv); + + if ((astat & BT848_INT_HLOCK) && btv->opt_automute) + audio_mux(btv, -1); + + if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { + printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->c.nr, + (astat & BT848_INT_SCERR) ? "SCERR" : "", + (astat & BT848_INT_OCERR) ? "OCERR" : "", + btread(BT848_RISC_COUNT)); + bttv_print_irqbits(stat,astat); + printk("\n"); + if (bttv_debug) + bttv_print_riscaddr(btv); } - + if (fdsr && astat & BT848_INT_FDSR) { + printk(KERN_INFO "bttv%d: FDSR @ %08x\n", + btv->c.nr,btread(BT848_RISC_COUNT)); + if (bttv_debug) + bttv_print_riscaddr(btv); + } + count++; - if (count > 20) { + if (count > 4) { btwrite(0, BT848_INT_MASK); printk(KERN_ERR - "bttv%d: IRQ lockup, cleared int mask\n", btv->nr); - bt848_offline(btv); + "bttv%d: IRQ lockup, cleared int mask [", btv->c.nr); + bttv_print_irqbits(stat,astat); + printk("]\n"); } } + btv->irq_total++; + if (handled) + btv->irq_me++; + return IRQ_RETVAL(handled); } +/* ----------------------------------------------------------------------- */ +/* initialitation */ -/* - * Scan for a Bt848 card, request the irq and map the io memory - */ +static struct video_device *vdev_init(struct bttv *btv, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; + snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", + btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", + type, bttv_tvcards[btv->c.type].name); + return vfd; +} + +static void bttv_unregister_video(struct bttv *btv) +{ + if (btv->video_dev) { + if (-1 != btv->video_dev->minor) + video_unregister_device(btv->video_dev); + else + video_device_release(btv->video_dev); + btv->video_dev = NULL; + } + if (btv->vbi_dev) { + if (-1 != btv->vbi_dev->minor) + video_unregister_device(btv->vbi_dev); + else + video_device_release(btv->vbi_dev); + btv->vbi_dev = NULL; + } + if (btv->radio_dev) { + if (-1 != btv->radio_dev->minor) + video_unregister_device(btv->radio_dev); + else + video_device_release(btv->radio_dev); + btv->radio_dev = NULL; + } +} -static void bttv_remove(struct pci_dev *pci_dev) +/* register video4linux devices */ +static int __devinit bttv_register_video(struct bttv *btv) { - u8 command; - unsigned int j; - struct bttv *btv = pci_get_drvdata(pci_dev); + /* video */ + btv->video_dev = vdev_init(btv, &bttv_video_template, "video"); + if (NULL == btv->video_dev) + goto err; + if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) + goto err; + printk(KERN_INFO "bttv%d: registered device video%d\n", + btv->c.nr,btv->video_dev->minor & 0x1f); - if (bttv_verbose) - printk("bttv%d: unloading\n",btv->nr); + /* vbi */ + btv->vbi_dev = vdev_init(btv, &bttv_vbi_template, "vbi"); + if (NULL == btv->vbi_dev) + goto err; + if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) + goto err; + printk(KERN_INFO "bttv%d: registered device vbi%d\n", + btv->c.nr,btv->vbi_dev->minor & 0x1f); - /* 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(~(u32)0,BT848_INT_STAT); - btwrite(0, BT848_GPIO_OUT_EN); - if (bttv_gpio) - bttv_gpio_tracking(btv,"cleanup"); + if (!btv->has_radio) + return 0; + /* radio */ + btv->radio_dev = vdev_init(btv, &radio_template, "radio"); + if (NULL == btv->radio_dev) + goto err; + if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) + goto err; + printk(KERN_INFO "bttv%d: registered device radio%d\n", + btv->c.nr,btv->radio_dev->minor & 0x1f); - /* 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->dev->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); + /* all done */ + return 0; - pci_set_drvdata(pci_dev, NULL); - return; + err: + bttv_unregister_video(btv); + return -1; } -static int __devinit bttv_probe(struct pci_dev *dev, const struct pci_device_id *pci_id) +/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ +/* response on cards with no firmware is not enabled by OF */ +static void pci_set_command(struct pci_dev *dev) { - int result; - unsigned char lat; - struct bttv *btv; #if defined(__powerpc__) unsigned int cmd; + + pci_read_config_dword(dev, PCI_COMMAND, &cmd); + cmd = (cmd | PCI_COMMAND_MEMORY ); + pci_write_config_dword(dev, PCI_COMMAND, cmd); #endif +} + +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 (bttv_num == BTTV_MAX) return -ENOMEM; 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)); + memset(btv,0,sizeof(*btv)); + btv->c.nr = bttv_num; + sprintf(btv->c.name,"bttv%d",btv->c.nr); + + /* initialize structs / fill in defaults */ + init_MUTEX(&btv->lock); + init_MUTEX(&btv->reslock); + btv->s_lock = SPIN_LOCK_UNLOCKED; + btv->gpio_lock = SPIN_LOCK_UNLOCKED; + init_waitqueue_head(&btv->gpioq); + init_waitqueue_head(&btv->i2c_queue); + INIT_LIST_HEAD(&btv->c.subs); + INIT_LIST_HEAD(&btv->capture); + INIT_LIST_HEAD(&btv->vcapture); +#ifdef VIDIOC_G_PRIORITY + v4l2_prio_init(&btv->prio); +#endif + + init_timer(&btv->timeout); + btv->timeout.function = bttv_irq_timeout; + btv->timeout.data = (unsigned long)btv; - btv->id=dev->device; - btv->bt848_adr=pci_resource_start(dev,0); + btv->i2c_rc = -1; + btv->tuner_type = UNSET; + btv->pinnacle_id = UNSET; + btv->new_input = UNSET; + btv->gpioirq = 1; + btv->has_radio=radio[btv->c.nr]; + + /* pci stuff (init, get irq/mmio, ... */ + btv->c.pci = dev; + btv->id = dev->device; if (pci_enable_device(dev)) { printk(KERN_WARNING "bttv%d: Can't enable device.\n", - btv->nr); + btv->c.nr); return -EIO; } if (pci_set_dma_mask(dev, 0xffffffff)) { printk(KERN_WARNING "bttv%d: No suitable DMA available.\n", - btv->nr); + btv->c.nr); return -EIO; } if (!request_mem_region(pci_resource_start(dev,0), pci_resource_len(dev,0), - "bttv")) { + btv->c.name)) { + printk(KERN_WARNING "bttv%d: can't request iomem (0x%lx).\n", + btv->c.nr, pci_resource_start(dev,0)); return -EBUSY; } - if (btv->id >= 878) - btv->i2c_command = 0x83; - else - btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); + pci_set_master(dev); + pci_set_command(dev); + pci_set_drvdata(dev,btv); + if (!pci_dma_supported(dev,0xffffffff)) { + printk("bttv%d: Oops: no 32bit PCI DMA ???\n", btv->c.nr); + result = -EIO; + goto fail1; + } 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(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ", + bttv_num,btv->id, btv->revision, pci_name(dev)); printk("irq: %d, latency: %d, mmio: 0x%lx\n", - btv->dev->irq, lat, btv->bt848_adr); + btv->c.pci->irq, lat, pci_resource_start(dev,0)); + schedule(); - bttv_idcard(btv); + btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000); + if (NULL == ioremap(pci_resource_start(dev,0), 0x1000)) { + printk("bttv%d: ioremap() failed\n", btv->c.nr); + result = -EIO; + goto fail1; + } -#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 + /* identify card */ + bttv_idcard(btv); -#ifdef __sparc__ - btv->bt848_mem=(unsigned char *)btv->bt848_adr; -#else - btv->bt848_mem=ioremap(btv->bt848_adr, 0x1000); -#endif - - /* clear interrupt mask */ + /* disable irqs, register irq handler */ btwrite(0, BT848_INT_MASK); - - result = request_irq(btv->dev->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); + result = request_irq(btv->c.pci->irq, bttv_irq, + SA_SHIRQ | SA_INTERRUPT,btv->c.name,(void *)btv); + if (result < 0) { + printk(KERN_ERR "bttv%d: can't get IRQ %d\n", + bttv_num,btv->c.pci->irq); goto fail1; } - if (result==-EBUSY) - { - printk(KERN_ERR "bttv%d: IRQ %d busy, change your PnP config in BIOS\n",bttv_num,btv->dev->irq); - goto fail1; - } - if (result < 0) - goto fail1; - + if (0 != bttv_handle_chipset(btv)) { - result = -1; + result = -EIO; goto fail2; - } - - pci_set_master(dev); - pci_set_drvdata(dev,btv); + } - if(init_bt848(btv) < 0) { - bttv_remove(dev); - return -EIO; + /* init options from insmod args */ + btv->opt_combfilter = combfilter; + btv->opt_lumafilter = lumafilter; + btv->opt_automute = automute; + btv->opt_chroma_agc = chroma_agc; + btv->opt_adc_crush = adc_crush; + btv->opt_vcr_hack = vcr_hack; + btv->opt_whitecrush_upper = whitecrush_upper; + btv->opt_whitecrush_lower = whitecrush_lower; + + /* fill struct bttv with some useful defaults */ + btv->init.btv = btv; + btv->init.ov.w.width = 320; + btv->init.ov.w.height = 240; + btv->init.fmt = format_by_palette(VIDEO_PALETTE_RGB24); + btv->init.width = 320; + btv->init.height = 240; + btv->init.lines = 16; + btv->input = 0; + + /* initialize hardware */ + if (bttv_gpio) + bttv_gpio_tracking(btv,"pre-init"); + + bttv_risc_init_main(btv); + init_bt848(btv); + + /* gpio */ + btwrite(0x00, BT848_GPIO_REG_INP); + btwrite(0x00, BT848_GPIO_OUT_EN); + if (bttv_verbose) + bttv_gpio_tracking(btv,"init"); + + /* needs to be done before i2c is registered */ + bttv_init_card1(btv); + + /* register i2c + gpio */ + init_bttv_i2c(btv); + + /* some card-specific stuff (needs working i2c) */ + bttv_init_card2(btv); + init_irqreg(btv); + + /* register video4linux + input */ + if (!bttv_tvcards[btv->c.type].no_video) { + bttv_register_video(btv); + bt848_bright(btv,32768); + bt848_contrast(btv,32768); + bt848_hue(btv,32768); + bt848_sat(btv,32768); + audio_mux(btv,AUDIO_MUTE); + set_input(btv,0); } - bttv_num++; + + /* everything is fine */ + bttv_num++; return 0; fail2: - free_irq(btv->dev->irq,btv); + free_irq(btv->c.pci->irq,btv); + fail1: - release_mem_region(pci_resource_start(btv->dev,0), - pci_resource_len(btv->dev,0)); + if (btv->bt848_mmio) + iounmap(btv->bt848_mmio); + release_mem_region(pci_resource_start(btv->c.pci,0), + pci_resource_len(btv->c.pci,0)); + pci_set_drvdata(dev,NULL); return result; } +static void __devexit bttv_remove(struct pci_dev *pci_dev) +{ + struct bttv *btv = pci_get_drvdata(pci_dev); + + if (bttv_verbose) + printk("bttv%d: unloading\n",btv->c.nr); + + /* shutdown everything (DMA+IRQs) */ + btand(~15, BT848_GPIO_DMA_CTL); + btwrite(0, BT848_INT_MASK); + btwrite(~0x0, BT848_INT_STAT); + btwrite(0x0, BT848_GPIO_OUT_EN); + if (bttv_gpio) + bttv_gpio_tracking(btv,"cleanup"); + + /* tell gpio modules we are leaving ... */ + btv->shutdown=1; + wake_up(&btv->gpioq); + + /* unregister i2c_bus + input */ + fini_bttv_i2c(btv); + + /* unregister video4linux */ + bttv_unregister_video(btv); + + /* free allocated memory */ + btcx_riscmem_free(btv->c.pci,&btv->main); + + /* free ressources */ + free_irq(btv->c.pci->irq,btv); + iounmap(btv->bt848_mmio); + release_mem_region(pci_resource_start(btv->c.pci,0), + pci_resource_len(btv->c.pci,0)); + + pci_set_drvdata(pci_dev, NULL); + return; +} + +static int bttv_suspend(struct pci_dev *pci_dev, u32 state) +{ + struct bttv *btv = pci_get_drvdata(pci_dev); + struct bttv_buffer_set idle; + unsigned long flags; + + printk("bttv%d: suspend %d\n", btv->c.nr, state); + + /* stop dma + irqs */ + spin_lock_irqsave(&btv->s_lock,flags); + memset(&idle, 0, sizeof(idle)); + btv->state.video = btv->curr; + btv->state.vbi = btv->cvbi; + btv->curr = idle; + bttv_buffer_activate_video(btv, &idle); + bttv_buffer_activate_vbi(btv, NULL); + bttv_set_dma(btv, 0, 0); + btwrite(0, BT848_INT_MASK); + spin_unlock_irqrestore(&btv->s_lock,flags); + + /* save bt878 state */ + btv->state.gpio_enable = btread(BT848_GPIO_OUT_EN); + btv->state.gpio_data = gpio_read(); + + /* save pci state */ + pci_save_state(pci_dev, btv->state.pci_cfg); + if (0 != pci_set_power_state(pci_dev, state)) { + pci_disable_device(pci_dev); + btv->state.disabled = 1; + } + return 0; +} + +static int bttv_resume(struct pci_dev *pci_dev) +{ + struct bttv *btv = pci_get_drvdata(pci_dev); + unsigned long flags; + + printk("bttv%d: resume\n", btv->c.nr); + + /* restore pci state */ + if (btv->state.disabled) { + pci_enable_device(pci_dev); + btv->state.disabled = 0; + } + pci_set_power_state(pci_dev, 0); + pci_restore_state(pci_dev, btv->state.pci_cfg); + + /* restore bt878 state */ + bttv_reinit_bt848(btv); + gpio_inout(0xffffff, btv->state.gpio_enable); + gpio_write(btv->state.gpio_data); + + /* restart dma */ + spin_lock_irqsave(&btv->s_lock,flags); + btv->curr = btv->state.video; + btv->cvbi = btv->state.vbi; + bttv_buffer_activate_video(btv, &btv->curr); + bttv_buffer_activate_vbi(btv, btv->cvbi); + bttv_set_dma(btv, 0, btv->curr.irqflags); + spin_unlock_irqrestore(&btv->s_lock,flags); + return 0; +} + static struct pci_device_id bttv_pci_tbl[] = { {PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, @@ -3165,7 +3981,10 @@ .name = "bttv", .id_table = bttv_pci_tbl, .probe = bttv_probe, - .remove = bttv_remove, + .remove = __devexit_p(bttv_remove), + + .suspend = bttv_suspend, + .resume = bttv_resume, }; static int bttv_init_module(void) @@ -3177,13 +3996,18 @@ (BTTV_VERSION_CODE >> 16) & 0xff, (BTTV_VERSION_CODE >> 8) & 0xff, BTTV_VERSION_CODE & 0xff); - if (gbuffers < 2 || gbuffers > MAX_GBUFFERS) +#ifdef SNAPSHOT + printk(KERN_INFO "bttv: snapshot date %04d-%02d-%02d\n", + SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100); +#endif + if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) gbuffers = 2; if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF) gbufsize = BTTV_MAX_FBUF; + gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; if (bttv_verbose) - printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n", - gbuffers,gbufsize/1024,gbuffers*gbufsize/1024); + printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n", + gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT); bttv_check_chipset(); diff -up linux-2.4.26/drivers/media/video/bttv-gpio.c linux/drivers/media/video/bttv-gpio.c --- linux-2.4.26/drivers/media/video/bttv-gpio.c 2004-04-21 14:11:41.000000000 +0200 +++ linux/drivers/media/video/bttv-gpio.c 2004-04-21 14:11:41.000000000 +0200 @@ -0,0 +1,183 @@ +/* + bttv-gpio.c -- gpio sub drivers + + sysfs-based sub driver interface for bttv + mainly intented for gpio access + + + Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) + & Marcus Metzler (mocm@thp.uni-koeln.de) + (c) 1999-2003 Gerd Knorr <kraxel@bytesex.org> + + 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/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <asm/io.h> + +#include "bttvp.h" + +/* ----------------------------------------------------------------------- */ +/* internal: the bttv "bus" */ + +static int bttv_sub_bus_match(struct device *dev, struct device_driver *drv) +{ + struct bttv_sub_driver *sub = to_bttv_sub_drv(drv); + int len = strlen(sub->wanted); + + if (0 == strncmp(dev->bus_id, sub->wanted, len)) + return 1; + return 0; +} + +struct bus_type bttv_sub_bus_type = { + .name = "bttv-sub", + .match = &bttv_sub_bus_match, +}; +EXPORT_SYMBOL(bttv_sub_bus_type); + +static void release_sub_device(struct device *dev) +{ + struct bttv_sub_device *sub = to_bttv_sub_dev(dev); + kfree(sub); +} + +int bttv_sub_add_device(struct bttv_core *core, char *name) +{ + struct bttv_sub_device *sub; + + sub = kmalloc(sizeof(*sub),GFP_KERNEL); + if (NULL == sub) + return -ENOMEM; + memset(sub,0,sizeof(*sub)); + + sub->core = core; + sub->dev.parent = &core->pci->dev; + sub->dev.bus = &bttv_sub_bus_type; + sub->dev.release = release_sub_device; + snprintf(sub->dev.bus_id,sizeof(sub->dev.bus_id),"%s%d", + name, core->nr); + + printk("bttv%d: add subdevice \"%s\"\n", core->nr, sub->dev.bus_id); + list_add_tail(&sub->list,&core->subs); + device_register(&sub->dev); + return 0; +} + +int bttv_sub_del_devices(struct bttv_core *core) +{ + struct bttv_sub_device *sub; + struct list_head *item,*save; + + list_for_each_safe(item,save,&core->subs) { + sub = list_entry(item,struct bttv_sub_device,list); + device_unregister(&sub->dev); + } + return 0; +} + +void bttv_gpio_irq(struct bttv_core *core) +{ + struct bttv_sub_driver *drv; + struct bttv_sub_device *dev; + struct list_head *item; + + list_for_each(item,&core->subs) { + dev = list_entry(item,struct bttv_sub_device,list); + drv = to_bttv_sub_drv(dev->dev.driver); + if (drv && drv->gpio_irq) + drv->gpio_irq(dev); + } +} + +/* ----------------------------------------------------------------------- */ +/* external: sub-driver register/unregister */ + +int bttv_sub_register(struct bttv_sub_driver *sub, char *wanted) +{ + sub->drv.bus = &bttv_sub_bus_type; + snprintf(sub->wanted,sizeof(sub->wanted),"%s",wanted); + driver_register(&sub->drv); + return 0; +} +EXPORT_SYMBOL(bttv_sub_register); + +int bttv_sub_unregister(struct bttv_sub_driver *sub) +{ + driver_unregister(&sub->drv); + return 0; +} +EXPORT_SYMBOL(bttv_sub_unregister); + +/* ----------------------------------------------------------------------- */ +/* external: gpio access functions */ + +void bttv_gpio_inout(struct bttv_core *core, u32 mask, u32 outbits) +{ + struct bttv *btv = container_of(core, struct bttv, c); + unsigned long flags; + u32 data; + + spin_lock_irqsave(&btv->gpio_lock,flags); + data = btread(BT848_GPIO_OUT_EN); + data = data & ~mask; + data = data | (mask & outbits); + btwrite(data,BT848_GPIO_OUT_EN); + spin_unlock_irqrestore(&btv->gpio_lock,flags); +} +EXPORT_SYMBOL(bttv_gpio_inout); + +u32 bttv_gpio_read(struct bttv_core *core) +{ + struct bttv *btv = container_of(core, struct bttv, c); + u32 value; + + value = btread(BT848_GPIO_DATA); + return value; +} +EXPORT_SYMBOL(bttv_gpio_read); + +void bttv_gpio_write(struct bttv_core *core, u32 value) +{ + struct bttv *btv = container_of(core, struct bttv, c); + + btwrite(value,BT848_GPIO_DATA); +} +EXPORT_SYMBOL(bttv_gpio_write); + +void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits) +{ + struct bttv *btv = container_of(core, struct bttv, c); + unsigned long flags; + u32 data; + + spin_lock_irqsave(&btv->gpio_lock,flags); + data = btread(BT848_GPIO_DATA); + data = data & ~mask; + data = data | (mask & bits); + btwrite(data,BT848_GPIO_DATA); + spin_unlock_irqrestore(&btv->gpio_lock,flags); +} +EXPORT_SYMBOL(bttv_gpio_bits); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -up linux-2.4.26/drivers/media/video/bttv-i2c.c linux/drivers/media/video/bttv-i2c.c --- linux-2.4.26/drivers/media/video/bttv-i2c.c 2004-04-21 14:11:41.000000000 +0200 +++ linux/drivers/media/video/bttv-i2c.c 2004-04-21 14:11:41.000000000 +0200 @@ -0,0 +1,457 @@ +/* + bttv-i2c.c -- all the i2c code is here + + 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-2003 Gerd Knorr <kraxel@bytesex.org> + + 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/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <asm/io.h> + +#include "bttvp.h" + +static struct i2c_algo_bit_data bttv_i2c_algo_bit_template; +static struct i2c_adapter bttv_i2c_adap_sw_template; +static struct i2c_adapter bttv_i2c_adap_hw_template; +static struct i2c_client bttv_i2c_client_template; + +static void bttv_inc_use(struct i2c_adapter *adap); +static void bttv_dec_use(struct i2c_adapter *adap); +static int attach_inform(struct i2c_client *client); + +static int i2c_debug = 0; +static int i2c_hw = 0; +MODULE_PARM(i2c_debug,"i"); +MODULE_PARM(i2c_hw,"i"); + +/* ----------------------------------------------------------------------- */ +/* I2C functions - bitbanging adapter (software i2c) */ + +void bttv_bit_setscl(void *data, int state) +{ + struct bttv *btv = (struct bttv*)data; + + if (state) + btv->i2c_state |= 0x02; + else + btv->i2c_state &= ~0x02; + btwrite(btv->i2c_state, BT848_I2C); + btread(BT848_I2C); +} + +void bttv_bit_setsda(void *data, int state) +{ + struct bttv *btv = (struct bttv*)data; + + if (state) + btv->i2c_state |= 0x01; + else + btv->i2c_state &= ~0x01; + btwrite(btv->i2c_state, BT848_I2C); + btread(BT848_I2C); +} + +static int bttv_bit_getscl(void *data) +{ + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x02 ? 1 : 0; + return state; +} + +static int bttv_bit_getsda(void *data) +{ + struct bttv *btv = (struct bttv*)data; + int state; + + state = btread(BT848_I2C) & 0x01; + return state; +} + +static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = { + .setsda = bttv_bit_setsda, + .setscl = bttv_bit_setscl, + .getsda = bttv_bit_getsda, + .getscl = bttv_bit_getscl, + .udelay = 16, + .mdelay = 10, + .timeout = 200, +}; + +static struct i2c_adapter bttv_i2c_adap_sw_template = { + .inc_use = bttv_inc_use, + .dec_use = bttv_dec_use, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif + I2C_DEVNAME("bt848"), + .id = I2C_HW_B_BT848, + .client_register = attach_inform, +}; + +/* ----------------------------------------------------------------------- */ +/* I2C functions - hardware i2c */ + +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + return 0; +} + +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +static int +bttv_i2c_wait_done(struct bttv *btv) +{ + DECLARE_WAITQUEUE(wait, current); + int rc = 0; + + add_wait_queue(&btv->i2c_queue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + if (0 == btv->i2c_done) + schedule_timeout(HZ/50+1); + set_current_state(TASK_RUNNING); + remove_wait_queue(&btv->i2c_queue, &wait); + + if (0 == btv->i2c_done) + /* timeout */ + rc = -EIO; + if (btv->i2c_done & BT848_INT_RACK) + rc = 1; + btv->i2c_done = 0; + return rc; +} + +#define I2C_HW (BT878_I2C_MODE | BT848_I2C_SYNC |\ + BT848_I2C_SCL | BT848_I2C_SDA) + +static int +bttv_i2c_sendbytes(struct bttv *btv, const struct i2c_msg *msg, int last) +{ + u32 xmit; + int retval,cnt; + + /* sanity checks */ + if (0 == msg->len) + return -EINVAL; + + /* start, address + first byte */ + xmit = (msg->addr << 25) | (msg->buf[0] << 16) | I2C_HW; + if (msg->len > 1 || !last) + xmit |= BT878_I2C_NOSTOP; + btwrite(xmit, BT848_I2C); + retval = bttv_i2c_wait_done(btv); + if (retval < 0) + goto err; + if (retval == 0) + goto eio; + if (i2c_debug) { + printk(" <W %02x %02x", msg->addr << 1, msg->buf[0]); + if (!(xmit & BT878_I2C_NOSTOP)) + printk(" >\n"); + } + + for (cnt = 1; cnt < msg->len; cnt++ ) { + /* following bytes */ + xmit = (msg->buf[cnt] << 24) | I2C_HW | BT878_I2C_NOSTART; + if (cnt < msg->len-1 || !last) + xmit |= BT878_I2C_NOSTOP; + btwrite(xmit, BT848_I2C); + retval = bttv_i2c_wait_done(btv); + if (retval < 0) + goto err; + if (retval == 0) + goto eio; + if (i2c_debug) { + printk(" %02x", msg->buf[cnt]); + if (!(xmit & BT878_I2C_NOSTOP)) + printk(" >\n"); + } + } + return msg->len; + + eio: + retval = -EIO; + err: + if (i2c_debug) + printk(" ERR: %d\n",retval); + return retval; +} + +static int +bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last) +{ + u32 xmit; + u32 cnt; + int retval; + + for(cnt = 0; cnt < msg->len; cnt++) { + xmit = (msg->addr << 25) | (1 << 24) | I2C_HW; + if (cnt < msg->len-1) + xmit |= BT848_I2C_W3B; + if (cnt < msg->len-1 || !last) + xmit |= BT878_I2C_NOSTOP; + if (cnt) + xmit |= BT878_I2C_NOSTART; + btwrite(xmit, BT848_I2C); + retval = bttv_i2c_wait_done(btv); + if (retval < 0) + goto err; + if (retval == 0) + goto eio; + msg->buf[cnt] = ((u32)btread(BT848_I2C) >> 8) & 0xff; + if (i2c_debug) { + if (!(xmit & BT878_I2C_NOSTART)) + printk(" <R %02x", (msg->addr << 1) +1); + printk(" =%02x", msg->buf[cnt]); + if (!(xmit & BT878_I2C_NOSTOP)) + printk(" >\n"); + } + } + return msg->len; + + eio: + retval = -EIO; + err: + if (i2c_debug) + printk(" ERR: %d\n",retval); + return retval; +} + +int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + struct bttv *btv = i2c_get_adapdata(i2c_adap); + int retval = 0; + int i; + + if (i2c_debug) + printk("bt-i2c:"); + btwrite(BT848_INT_I2CDONE|BT848_INT_RACK, BT848_INT_STAT); + for (i = 0 ; i < num; i++) { + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = bttv_i2c_readbytes(btv, &msgs[i], i+1 == num); + if (retval < 0) + goto err; + } else { + /* write */ + retval = bttv_i2c_sendbytes(btv, &msgs[i], i+1 == num); + if (retval < 0) + goto err; + } + } + return num; + + err: + return retval; +} + +static struct i2c_algorithm bttv_algo = { + .name = "bt878", + .id = I2C_ALGO_BIT | I2C_HW_B_BT848 /* FIXME */, + .master_xfer = bttv_i2c_xfer, + .algo_control = algo_control, + .functionality = functionality, +}; + +static struct i2c_adapter bttv_i2c_adap_hw_template = { + .inc_use = bttv_inc_use, + .dec_use = bttv_dec_use, +#ifdef I2C_ADAP_CLASS_TV_ANALOG + .class = I2C_ADAP_CLASS_TV_ANALOG, +#endif + I2C_DEVNAME("bt878"), + .id = I2C_ALGO_BIT | I2C_HW_B_BT848 /* FIXME */, + .algo = &bttv_algo, + .client_register = attach_inform, +}; + +/* ----------------------------------------------------------------------- */ +/* I2C functions - common stuff */ + +static void bttv_inc_use(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bttv_dec_use(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +static int attach_inform(struct i2c_client *client) +{ + struct bttv *btv = i2c_get_adapdata(client->adapter); + + if (btv->tuner_type != UNSET) + bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); + if (btv->pinnacle_id != UNSET) + bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE, + &btv->pinnacle_id); + + if (bttv_debug) + printk("bttv%d: i2c attach [client=%s]\n", + btv->c.nr, i2c_clientname(client)); + return 0; +} + +void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) +{ + if (0 != btv->i2c_rc) + return; + i2c_clients_command(&btv->c.i2c_adap, cmd, arg); +} + +void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg) +{ + if (card >= bttv_num) + return; + bttv_call_i2c_clients(&bttvs[card], cmd, arg); +} + +static struct i2c_client bttv_i2c_client_template = { + I2C_DEVNAME("bttv internal"), + .id = -1, +}; + + +/* read I2C */ +int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) +{ + unsigned char buffer = 0; + + if (0 != btv->i2c_rc) + return -1; + if (bttv_verbose && NULL != probe_for) + printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ", + btv->c.nr,probe_for,addr); + btv->i2c_client.addr = addr >> 1; + if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) { + if (NULL != probe_for) { + if (bttv_verbose) + printk("not found\n"); + } else + printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n", + btv->c.nr,addr); + return -1; + } + if (bttv_verbose && NULL != probe_for) + printk("found\n"); + return buffer; +} + +/* write I2C */ +int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, + unsigned char b2, int both) +{ + unsigned char buffer[2]; + int bytes = both ? 2 : 1; + + if (0 != btv->i2c_rc) + return -1; + btv->i2c_client.addr = addr >> 1; + buffer[0] = b1; + buffer[1] = b2; + if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes)) + return -1; + return 0; +} + +/* read EEPROM content */ +void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr) +{ + int i; + + if (bttv_I2CWrite(btv, addr, 0, -1, 0)<0) { + printk(KERN_WARNING "bttv: readee error\n"); + return; + } + btv->i2c_client.addr = addr >> 1; + for (i=0; i<256; i+=16) { + if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) { + printk(KERN_WARNING "bttv: readee error\n"); + break; + } + } +} + +/* init + register i2c algo-bit adapter */ +int __devinit init_bttv_i2c(struct bttv *btv) +{ + memcpy(&btv->i2c_client, &bttv_i2c_client_template, + sizeof(bttv_i2c_client_template)); + + if (i2c_hw) + btv->use_i2c_hw = 1; + if (btv->use_i2c_hw) { + /* bt878 */ + memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_hw_template, + sizeof(bttv_i2c_adap_hw_template)); + } else { + /* bt848 */ + memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_sw_template, + sizeof(bttv_i2c_adap_sw_template)); + memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template, + sizeof(bttv_i2c_algo_bit_template)); + btv->i2c_algo.data = btv; + btv->c.i2c_adap.algo_data = &btv->i2c_algo; + } + + snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name), + "bt%d #%d [%s]", btv->id, btv->c.nr, + btv->use_i2c_hw ? "hw" : "sw"); + + i2c_set_adapdata(&btv->c.i2c_adap, btv); + btv->i2c_client.adapter = &btv->c.i2c_adap; + + if (btv->use_i2c_hw) { + btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap); + } else { + bttv_bit_setscl(btv,1); + bttv_bit_setsda(btv,1); + btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap); + } + return btv->i2c_rc; +} + +int __devexit fini_bttv_i2c(struct bttv *btv) +{ + if (0 != btv->i2c_rc) + return 0; + + if (btv->use_i2c_hw) { + return i2c_del_adapter(&btv->c.i2c_adap); + } else { + return i2c_bit_del_bus(&btv->c.i2c_adap); + } +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ --- linux-2.4.29/drivers/media/video/bttv-if.c.orig 2005-01-19 15:09:56.000000000 +0100 +++ linux-2.4.29/drivers/media/video/bttv-if.c 2005-01-20 14:40:48.000000000 +0100 @@ -1,7 +1,7 @@ /* - bttv-if.c -- interfaces to other kernel modules - all the i2c code is here - also the gpio interface exported by bttv (used by lirc) + bttv-if.c -- old gpio interface to other kernel modules + don't use in new code, will go away in 2.7 + have a look at bttv-gpio.c instead. bttv - Bt848 frame grabber driver @@ -27,15 +27,11 @@ #include <linux/module.h> #include <linux/init.h> - +#include <linux/delay.h> #include <asm/io.h> #include "bttvp.h" -static struct i2c_algo_bit_data bttv_i2c_algo_template; -static struct i2c_adapter bttv_i2c_adap_template; -static struct i2c_client bttv_i2c_client_template; - EXPORT_SYMBOL(bttv_get_cardinfo); EXPORT_SYMBOL(bttv_get_pcidev); EXPORT_SYMBOL(bttv_get_id); @@ -55,7 +51,7 @@ if (card >= bttv_num) { return -1; } - *type = bttvs[card].type; + *type = bttvs[card].c.type; *cardid = bttvs[card].cardid; return 0; } @@ -64,7 +60,7 @@ { if (card >= bttv_num) return NULL; - return bttvs[card].dev; + return bttvs[card].c.pci; } int bttv_get_id(unsigned int card) @@ -73,9 +69,19 @@ if (card >= bttv_num) { return -1; } - return bttvs[card].type; + return bttvs[card].c.type; } +/* with 2.6.x not needed thanks to the driver model + sysfs */ +struct i2c_adapter *bttv_get_i2c_adap(unsigned int card) +{ + if (card >= bttv_num) { + return NULL; + } + return &bttvs[card].c.i2c_adap; +} +EXPORT_SYMBOL(bttv_get_i2c_adap); + int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data) { struct bttv *btv; @@ -85,7 +91,7 @@ } btv = &bttvs[card]; - btaor(data, ~mask, BT848_GPIO_OUT_EN); + gpio_inout(mask,data); if (bttv_gpio) bttv_gpio_tracking(btv,"extern enable"); return 0; @@ -107,7 +113,7 @@ /* prior setting BT848_GPIO_REG_INP is (probably) not needed because we set direct input on init */ - *data = btread(BT848_GPIO_DATA); + *data = gpio_read(); return 0; } @@ -123,7 +129,7 @@ /* prior setting BT848_GPIO_REG_INP is (probably) not needed because direct input is set on init */ - btaor(data & mask, ~mask, BT848_GPIO_DATA); + gpio_bits(mask,data); if (bttv_gpio) bttv_gpio_tracking(btv,"extern write"); return 0; @@ -144,203 +150,6 @@ return &btv->gpioq; } - -/* ----------------------------------------------------------------------- */ -/* I2C functions */ - -void bttv_bit_setscl(void *data, int state) -{ - struct bttv *btv = (struct bttv*)data; - - if (state) - btv->i2c_state |= 0x02; - else - btv->i2c_state &= ~0x02; - btwrite(btv->i2c_state, BT848_I2C); - btread(BT848_I2C); -} - -void bttv_bit_setsda(void *data, int state) -{ - struct bttv *btv = (struct bttv*)data; - - if (state) - btv->i2c_state |= 0x01; - else - btv->i2c_state &= ~0x01; - btwrite(btv->i2c_state, BT848_I2C); - btread(BT848_I2C); -} - -static int bttv_bit_getscl(void *data) -{ - struct bttv *btv = (struct bttv*)data; - int state; - - state = btread(BT848_I2C) & 0x02 ? 1 : 0; - return state; -} - -static int bttv_bit_getsda(void *data) -{ - struct bttv *btv = (struct bttv*)data; - int state; - - state = btread(BT848_I2C) & 0x01; - return state; -} - -static void bttv_inc_use(struct i2c_adapter *adap) -{ - MOD_INC_USE_COUNT; -} - -static void bttv_dec_use(struct i2c_adapter *adap) -{ - MOD_DEC_USE_COUNT; -} - -static int attach_inform(struct i2c_client *client) -{ - struct bttv *btv = i2c_get_adapdata(client->adapter); - - if (btv->tuner_type != UNSET) - bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); - if (btv->pinnacle_id != UNSET) - bttv_call_i2c_clients(btv,AUDC_CONFIG_PINNACLE, - &btv->pinnacle_id); - - if (bttv_debug) - printk("bttv%d: i2c attach [client=%s]\n", - btv->nr, i2c_clientname(client)); - return 0; -} - -void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg) -{ - if (0 != btv->i2c_rc) - return; - i2c_clients_command(&btv->i2c_adap, cmd, arg); -} - -void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg) -{ - if (card >= bttv_num) - return; - bttv_call_i2c_clients(&bttvs[card], cmd, arg); -} - -static struct i2c_algo_bit_data bttv_i2c_algo_template = { - .setsda = bttv_bit_setsda, - .setscl = bttv_bit_setscl, - .getsda = bttv_bit_getsda, - .getscl = bttv_bit_getscl, - .udelay = 16, - .mdelay = 10, - .timeout = 200, -}; - -static struct i2c_adapter bttv_i2c_adap_template = { - .inc_use = bttv_inc_use, - .dec_use = bttv_dec_use, -#ifdef I2C_ADAP_CLASS_TV_ANALOG - .class = I2C_ADAP_CLASS_TV_ANALOG, -#endif - I2C_DEVNAME("bt848"), - .id = I2C_HW_B_BT848, - .client_register = attach_inform, -}; - -static struct i2c_client bttv_i2c_client_template = { - I2C_DEVNAME("bttv internal"), - .id = -1, -}; - - -/* read I2C */ -int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for) -{ - unsigned char buffer = 0; - - if (0 != btv->i2c_rc) - return -1; - if (bttv_verbose && NULL != probe_for) - printk(KERN_INFO "bttv%d: i2c: checking for %s @ 0x%02x... ", - btv->nr,probe_for,addr); - btv->i2c_client.addr = addr >> 1; - if (1 != i2c_master_recv(&btv->i2c_client, &buffer, 1)) { - if (NULL != probe_for) { - if (bttv_verbose) - printk("not found\n"); - } else - printk(KERN_WARNING "bttv%d: i2c read 0x%x: error\n", - btv->nr,addr); - return -1; - } - if (bttv_verbose && NULL != probe_for) - printk("found\n"); - return buffer; -} - -/* write I2C */ -int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1, - unsigned char b2, int both) -{ - unsigned char buffer[2]; - int bytes = both ? 2 : 1; - - if (0 != btv->i2c_rc) - return -1; - btv->i2c_client.addr = addr >> 1; - buffer[0] = b1; - buffer[1] = b2; - if (bytes != i2c_master_send(&btv->i2c_client, buffer, bytes)) - return -1; - return 0; -} - -/* read EEPROM content */ -void __devinit bttv_readee(struct bttv *btv, unsigned char *eedata, int addr) -{ - int i; - - if (bttv_I2CWrite(btv, addr, 0, -1, 0)<0) { - printk(KERN_WARNING "bttv: readee error\n"); - return; - } - btv->i2c_client.addr = addr >> 1; - for (i=0; i<256; i+=16) { - if (16 != i2c_master_recv(&btv->i2c_client,eedata+i,16)) { - printk(KERN_WARNING "bttv: readee error\n"); - break; - } - } -} - -/* init + register i2c algo-bit adapter */ -int __devinit init_bttv_i2c(struct bttv *btv) -{ - memcpy(&btv->i2c_adap, &bttv_i2c_adap_template, - sizeof(struct i2c_adapter)); - memcpy(&btv->i2c_algo, &bttv_i2c_algo_template, - sizeof(struct i2c_algo_bit_data)); - memcpy(&btv->i2c_client, &bttv_i2c_client_template, - sizeof(struct i2c_client)); - - sprintf(btv->i2c_adap.name, "bt848 #%d", btv->nr); - - btv->i2c_algo.data = btv; - i2c_set_adapdata(&btv->i2c_adap, btv); - btv->i2c_adap.algo_data = &btv->i2c_algo; - btv->i2c_client.adapter = &btv->i2c_adap; - - bttv_bit_setscl(btv,1); - bttv_bit_setsda(btv,1); - - btv->i2c_rc = i2c_bit_add_bus(&btv->i2c_adap); - return btv->i2c_rc; -} - /* * Local variables: * c-basic-offset: 8 diff -up linux-2.4.26/drivers/media/video/bttv-risc.c linux/drivers/media/video/bttv-risc.c --- linux-2.4.26/drivers/media/video/bttv-risc.c 2004-04-21 14:11:41.000000000 +0200 +++ linux/drivers/media/video/bttv-risc.c 2004-04-21 14:11:41.000000000 +0200 @@ -0,0 +1,778 @@ +/* + bttv-risc.c -- interfaces to other kernel modules + + bttv risc code handling + - memory management + - generation + + (c) 2000-2003 Gerd Knorr <kraxel@bytesex.org> + + 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/module.h> +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <asm/page.h> +#include <asm/pgtable.h> + +#include "bttvp.h" + +#define VCR_HACK_LINES 4 + +/* ---------------------------------------------------------- */ +/* risc code generators */ + +int +bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int offset, unsigned int bpl, + unsigned int padding, unsigned int lines) +{ + u32 instructions,line,todo; + struct scatterlist *sg; + u32 *rp; + int rc; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line + sync + jump (all 2 dwords) */ + instructions = (bpl * lines) / PAGE_SIZE + lines; + instructions += 2; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) + return rc; + + /* sync instruction */ + rp = risc->cpu; + *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(rp++) = cpu_to_le32(0); + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + if ((btv->opt_vcr_hack) && + (line >= (lines - VCR_HACK_LINES))) + continue; + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg++; + } + if (bpl <= sg_dma_len(sg)-offset) { + /* fits into current chunk */ + *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| + BT848_RISC_EOL|bpl); + *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); + offset+=bpl; + } else { + /* scanline needs to be splitted */ + todo = bpl; + *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| + (sg_dma_len(sg)-offset)); + *(rp++)=cpu_to_le32(sg_dma_address(sg)+offset); + todo -= (sg_dma_len(sg)-offset); + offset = 0; + sg++; + while (todo > sg_dma_len(sg)) { + *(rp++)=cpu_to_le32(BT848_RISC_WRITE| + sg_dma_len(sg)); + *(rp++)=cpu_to_le32(sg_dma_address(sg)); + todo -= sg_dma_len(sg); + sg++; + } + *(rp++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_EOL| + todo); + *(rp++)=cpu_to_le32(sg_dma_address(sg)); + offset += todo; + } + offset += padding; + } + dprintk("bttv%d: risc planar: %d sglist elems\n", btv->c.nr, (int)(sg-sglist)); + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + return 0; +} + +int +bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int yoffset, unsigned int ybpl, + unsigned int ypadding, unsigned int ylines, + unsigned int uoffset, unsigned int voffset, + unsigned int hshift, unsigned int vshift, + unsigned int cpadding) +{ + unsigned int instructions,line,todo,ylen,chroma; + u32 *rp,ri; + struct scatterlist *ysg; + struct scatterlist *usg; + struct scatterlist *vsg; + int rc; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line (5 dwords) + plus sync + jump (2 dwords) */ + instructions = (ybpl * ylines * 2) / PAGE_SIZE + ylines; + instructions += 2; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*4*5)) < 0) + return rc; + + /* sync instruction */ + rp = risc->cpu; + *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); + *(rp++) = cpu_to_le32(0); + + /* scan lines */ + ysg = sglist; + usg = sglist; + vsg = sglist; + for (line = 0; line < ylines; line++) { + if ((btv->opt_vcr_hack) && + (line >= (ylines - VCR_HACK_LINES))) + continue; + switch (vshift) { + case 0: chroma = 1; break; + case 1: chroma = !(line & 1); break; + case 2: chroma = !(line & 3); break; + default: chroma = 0; + } + for (todo = ybpl; todo > 0; todo -= ylen) { + /* go to next sg entry if needed */ + while (yoffset && yoffset >= sg_dma_len(ysg)) { + yoffset -= sg_dma_len(ysg); + ysg++; + } + while (uoffset && uoffset >= sg_dma_len(usg)) { + uoffset -= sg_dma_len(usg); + usg++; + } + while (voffset && voffset >= sg_dma_len(vsg)) { + voffset -= sg_dma_len(vsg); + vsg++; + } + + /* calculate max number of bytes we can write */ + ylen = todo; + if (yoffset + ylen > sg_dma_len(ysg)) + ylen = sg_dma_len(ysg) - yoffset; + if (chroma) { + if (uoffset + (ylen>>hshift) > sg_dma_len(usg)) + ylen = (sg_dma_len(usg) - uoffset) << hshift; + if (voffset + (ylen>>hshift) > sg_dma_len(vsg)) + ylen = (sg_dma_len(vsg) - voffset) << hshift; + ri = BT848_RISC_WRITE123; + } else { + ri = BT848_RISC_WRITE1S23; + } + if (ybpl == todo) + ri |= BT848_RISC_SOL; + if (ylen == todo) + ri |= BT848_RISC_EOL; + + /* write risc instruction */ + *(rp++)=cpu_to_le32(ri | ylen); + *(rp++)=cpu_to_le32(((ylen >> hshift) << 16) | + (ylen >> hshift)); + *(rp++)=cpu_to_le32(sg_dma_address(ysg)+yoffset); + yoffset += ylen; + if (chroma) { + *(rp++)=cpu_to_le32(sg_dma_address(usg)+uoffset); + uoffset += ylen >> hshift; + *(rp++)=cpu_to_le32(sg_dma_address(vsg)+voffset); + voffset += ylen >> hshift; + } + } + yoffset += ypadding; + if (chroma) { + uoffset += cpadding; + voffset += cpadding; + } + } + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + return 0; +} + +int +bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, + const struct bttv_format *fmt, struct bttv_overlay *ov, + int skip_even, int skip_odd) +{ + int instructions,rc,line,maxy,start,end,skip,nskips; + struct btcx_skiplist *skips; + u32 *rp,ri,ra; + u32 addr; + + /* skip list for window clipping */ + if (NULL == (skips = kmalloc(sizeof(*skips) * ov->nclips,GFP_KERNEL))) + return -ENOMEM; + + /* estimate risc mem: worst case is (clip+1) * lines instructions + + sync + jump (all 2 dwords) */ + instructions = (ov->nclips + 1) * + ((skip_even || skip_odd) ? ov->w.height>>1 : ov->w.height); + instructions += 2; + if ((rc = btcx_riscmem_alloc(btv->c.pci,risc,instructions*8)) < 0) { + kfree(skips); + return rc; + } + + /* sync instruction */ + rp = risc->cpu; + *(rp++) = cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); + *(rp++) = cpu_to_le32(0); + + addr = (unsigned long)btv->fbuf.base; + addr += btv->fbuf.fmt.bytesperline * ov->w.top; + addr += (fmt->depth >> 3) * ov->w.left; + + /* scan lines */ + for (maxy = -1, line = 0; line < ov->w.height; + line++, addr += btv->fbuf.fmt.bytesperline) { + if ((btv->opt_vcr_hack) && + (line >= (ov->w.height - VCR_HACK_LINES))) + continue; + if ((line%2) == 0 && skip_even) + continue; + if ((line%2) == 1 && skip_odd) + continue; + + /* calculate clipping */ + if (line > maxy) + btcx_calc_skips(line, ov->w.width, &maxy, + skips, &nskips, ov->clips, ov->nclips); + + /* write out risc code */ + for (start = 0, skip = 0; start < ov->w.width; start = end) { + if (skip >= nskips) { + ri = BT848_RISC_WRITE; + end = ov->w.width; + } else if (start < skips[skip].start) { + ri = BT848_RISC_WRITE; + end = skips[skip].start; + } else { + ri = BT848_RISC_SKIP; + end = skips[skip].end; + skip++; + } + if (BT848_RISC_WRITE == ri) + ra = addr + (fmt->depth>>3)*start; + else + ra = 0; + + if (0 == start) + ri |= BT848_RISC_SOL; + if (ov->w.width == end) + ri |= BT848_RISC_EOL; + ri |= (fmt->depth>>3) * (end-start); + + *(rp++)=cpu_to_le32(ri); + if (0 != ra) + *(rp++)=cpu_to_le32(ra); + } + } + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + kfree(skips); + return 0; +} + +/* ---------------------------------------------------------- */ + +void +bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, + int width, int height, int interleaved, int norm) +{ + const struct bttv_tvnorm *tvnorm = &bttv_tvnorms[norm]; + u32 xsf, sr; + int vdelay; + + int swidth = tvnorm->swidth; + int totalwidth = tvnorm->totalwidth; + int scaledtwidth = tvnorm->scaledtwidth; + + if (bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) { + swidth = 720; + totalwidth = 858; + scaledtwidth = 858; + } + + vdelay = tvnorm->vdelay; +#if 0 /* FIXME */ + if (vdelay < btv->vbi.lines*2) + vdelay = btv->vbi.lines*2; +#endif + + xsf = (width*scaledtwidth)/swidth; + geo->hscale = ((totalwidth*4096UL)/xsf-4096); + geo->hdelay = tvnorm->hdelayx1; + geo->hdelay = (geo->hdelay*width)/swidth; + geo->hdelay &= 0x3fe; + sr = ((tvnorm->sheight >> (interleaved?0:1))*512)/height - 512; + geo->vscale = (0x10000UL-sr) & 0x1fff; + geo->crop = ((width>>8)&0x03) | ((geo->hdelay>>6)&0x0c) | + ((tvnorm->sheight>>4)&0x30) | ((vdelay>>2)&0xc0); + geo->vscale |= interleaved ? (BT848_VSCALE_INT<<8) : 0; + geo->vdelay = vdelay; + geo->width = width; + geo->sheight = tvnorm->sheight; + geo->vtotal = tvnorm->vtotal; + + if (btv->opt_combfilter) { + geo->vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); + geo->comb = (width < 769) ? 1 : 0; + } else { + geo->vtc = 0; + geo->comb = 0; + } +} + +void +bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int odd) +{ + int off = odd ? 0x80 : 0x00; + + if (geo->comb) + btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); + else + btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); + + btwrite(geo->vtc, BT848_E_VTC+off); + btwrite(geo->hscale >> 8, BT848_E_HSCALE_HI+off); + btwrite(geo->hscale & 0xff, BT848_E_HSCALE_LO+off); + btaor((geo->vscale>>8), 0xe0, BT848_E_VSCALE_HI+off); + btwrite(geo->vscale & 0xff, BT848_E_VSCALE_LO+off); + btwrite(geo->width & 0xff, BT848_E_HACTIVE_LO+off); + btwrite(geo->hdelay & 0xff, BT848_E_HDELAY_LO+off); + btwrite(geo->sheight & 0xff, BT848_E_VACTIVE_LO+off); + btwrite(geo->vdelay & 0xff, BT848_E_VDELAY_LO+off); + btwrite(geo->crop, BT848_E_CROP+off); + btwrite(geo->vtotal>>8, BT848_VTOTAL_HI); + btwrite(geo->vtotal & 0xff, BT848_VTOTAL_LO); +} + +/* ---------------------------------------------------------- */ +/* risc group / risc main loop / dma management */ + +void +bttv_set_dma(struct bttv *btv, int override, int irqflags) +{ + unsigned long cmd; + int capctl; + + btv->cap_ctl = 0; + if (NULL != btv->curr.top) btv->cap_ctl |= 0x02; + if (NULL != btv->curr.bottom) btv->cap_ctl |= 0x01; + if (NULL != btv->cvbi) btv->cap_ctl |= 0x0c; + + capctl = 0; + capctl |= (btv->cap_ctl & 0x03) ? 0x03 : 0x00; /* capture */ + capctl |= (btv->cap_ctl & 0x0c) ? 0x0c : 0x00; /* vbi data */ + capctl |= override; + + d2printk(KERN_DEBUG + "bttv%d: capctl=%x irq=%d top=%08Lx/%08Lx even=%08Lx/%08Lx\n", + btv->c.nr,capctl,irqflags, + btv->cvbi ? (unsigned long long)btv->cvbi->top.dma : 0, + btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, + btv->cvbi ? (unsigned long long)btv->cvbi->bottom.dma : 0, + btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); + + cmd = BT848_RISC_JUMP; + if (irqflags) { + cmd |= BT848_RISC_IRQ; + cmd |= (irqflags & 0x0f) << 16; + cmd |= (~irqflags & 0x0f) << 20; + } + if (irqflags || btv->cvbi) { + mod_timer(&btv->timeout, jiffies+BTTV_TIMEOUT); + } else { + del_timer(&btv->timeout); + } + btv->main.cpu[RISC_SLOT_LOOP] = cpu_to_le32(cmd); + + btaor(capctl, ~0x0f, BT848_CAP_CTL); + if (capctl) { + if (btv->dma_on) + return; + btwrite(btv->main.dma, BT848_RISC_STRT_ADD); + btor(3, BT848_GPIO_DMA_CTL); + btv->dma_on = 1; + } else { + if (!btv->dma_on) + return; + btand(~3, BT848_GPIO_DMA_CTL); + btv->dma_on = 0; + } + return; +} + +int +bttv_risc_init_main(struct bttv *btv) +{ + int rc; + + if ((rc = btcx_riscmem_alloc(btv->c.pci,&btv->main,PAGE_SIZE)) < 0) + return rc; + dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n", + btv->c.nr,(unsigned long long)btv->main.dma); + + btv->main.cpu[0] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC | + BT848_FIFO_STATUS_VRE); + btv->main.cpu[1] = cpu_to_le32(0); + btv->main.cpu[2] = cpu_to_le32(BT848_RISC_JUMP); + btv->main.cpu[3] = cpu_to_le32(btv->main.dma + (4<<2)); + + /* top field */ + btv->main.cpu[4] = cpu_to_le32(BT848_RISC_JUMP); + btv->main.cpu[5] = cpu_to_le32(btv->main.dma + (6<<2)); + btv->main.cpu[6] = cpu_to_le32(BT848_RISC_JUMP); + btv->main.cpu[7] = cpu_to_le32(btv->main.dma + (8<<2)); + + btv->main.cpu[8] = cpu_to_le32(BT848_RISC_SYNC | BT848_RISC_RESYNC | + BT848_FIFO_STATUS_VRO); + btv->main.cpu[9] = cpu_to_le32(0); + + /* bottom field */ + btv->main.cpu[10] = cpu_to_le32(BT848_RISC_JUMP); + btv->main.cpu[11] = cpu_to_le32(btv->main.dma + (12<<2)); + btv->main.cpu[12] = cpu_to_le32(BT848_RISC_JUMP); + btv->main.cpu[13] = cpu_to_le32(btv->main.dma + (14<<2)); + + /* jump back to top field */ + btv->main.cpu[14] = cpu_to_le32(BT848_RISC_JUMP); + btv->main.cpu[15] = cpu_to_le32(btv->main.dma + (0<<2)); + + return 0; +} + +int +bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, + int irqflags) +{ + unsigned long cmd; + unsigned long next = btv->main.dma + ((slot+2) << 2); + + if (NULL == risc) { + d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=NULL\n", + btv->c.nr,risc,slot); + btv->main.cpu[slot+1] = cpu_to_le32(next); + } else { + d2printk(KERN_DEBUG "bttv%d: risc=%p slot[%d]=%08Lx irq=%d\n", + btv->c.nr,risc,slot,(unsigned long long)risc->dma,irqflags); + cmd = BT848_RISC_JUMP; + if (irqflags) { + cmd |= BT848_RISC_IRQ; + cmd |= (irqflags & 0x0f) << 16; + cmd |= (~irqflags & 0x0f) << 20; + } + risc->jmp[0] = cpu_to_le32(cmd); + risc->jmp[1] = cpu_to_le32(next); + btv->main.cpu[slot+1] = cpu_to_le32(risc->dma); + } + return 0; +} + +void +bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf) +{ + if (in_interrupt()) + BUG(); + videobuf_waiton(&buf->vb,0,0); + videobuf_dma_pci_unmap(btv->c.pci, &buf->vb.dma); + videobuf_dma_free(&buf->vb.dma); + btcx_riscmem_free(btv->c.pci,&buf->bottom); + btcx_riscmem_free(btv->c.pci,&buf->top); + buf->vb.state = STATE_NEEDS_INIT; +} + +int +bttv_buffer_activate_vbi(struct bttv *btv, + struct bttv_buffer *vbi) +{ + /* vbi capture */ + if (vbi) { + vbi->vb.state = STATE_ACTIVE; + list_del(&vbi->vb.queue); + bttv_risc_hook(btv, RISC_SLOT_O_VBI, &vbi->top, 0); + bttv_risc_hook(btv, RISC_SLOT_E_VBI, &vbi->bottom, 4); + } else { + bttv_risc_hook(btv, RISC_SLOT_O_VBI, NULL, 0); + bttv_risc_hook(btv, RISC_SLOT_E_VBI, NULL, 0); + } + return 0; +} + +int +bttv_buffer_activate_video(struct bttv *btv, + struct bttv_buffer_set *set) +{ + /* video capture */ + if (NULL != set->top && NULL != set->bottom) { + if (set->top == set->bottom) { + set->top->vb.state = STATE_ACTIVE; + if (set->top->vb.queue.next) + list_del(&set->top->vb.queue); + } else { + set->top->vb.state = STATE_ACTIVE; + set->bottom->vb.state = STATE_ACTIVE; + if (set->top->vb.queue.next) + list_del(&set->top->vb.queue); + if (set->bottom->vb.queue.next) + list_del(&set->bottom->vb.queue); + } + bttv_apply_geo(btv, &set->top->geo, 1); + bttv_apply_geo(btv, &set->bottom->geo,0); + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, set->topirq); + bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom, 0); + btaor((set->top->btformat & 0xf0) | (set->bottom->btformat & 0x0f), + ~0xff, BT848_COLOR_FMT); + btaor((set->top->btswap & 0x0a) | (set->bottom->btswap & 0x05), + ~0x0f, BT848_COLOR_CTL); + } else if (NULL != set->top) { + set->top->vb.state = STATE_ACTIVE; + if (set->top->vb.queue.next) + list_del(&set->top->vb.queue); + bttv_apply_geo(btv, &set->top->geo,1); + bttv_apply_geo(btv, &set->top->geo,0); + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &set->top->top, 0); + bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0); + btaor(set->top->btformat & 0xff, ~0xff, BT848_COLOR_FMT); + btaor(set->top->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); + } else if (NULL != set->bottom) { + set->bottom->vb.state = STATE_ACTIVE; + if (set->bottom->vb.queue.next) + list_del(&set->bottom->vb.queue); + bttv_apply_geo(btv, &set->bottom->geo,1); + bttv_apply_geo(btv, &set->bottom->geo,0); + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); + bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &set->bottom->bottom, 0); + btaor(set->bottom->btformat & 0xff, ~0xff, BT848_COLOR_FMT); + btaor(set->bottom->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL); + } else { + bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); + bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0); + } + return 0; +} + +/* ---------------------------------------------------------- */ + +/* calculate geometry, build risc code */ +int +bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) +{ + const struct bttv_tvnorm *tvnorm = bttv_tvnorms + buf->tvnorm; + + dprintk(KERN_DEBUG + "bttv%d: buffer field: %s format: %s size: %dx%d\n", + btv->c.nr, v4l2_field_names[buf->vb.field], + buf->fmt->name, buf->vb.width, buf->vb.height); + + /* packed pixel modes */ + if (buf->fmt->flags & FORMAT_FLAGS_PACKED) { + int bpl = (buf->fmt->depth >> 3) * buf->vb.width; + int bpf = bpl * (buf->vb.height >> 1); + + bttv_calc_geo(btv,&buf->geo,buf->vb.width,buf->vb.height, + V4L2_FIELD_HAS_BOTH(buf->vb.field),buf->tvnorm); + + switch (buf->vb.field) { + case V4L2_FIELD_TOP: + bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, + 0,bpl,0,buf->vb.height); + break; + case V4L2_FIELD_BOTTOM: + bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, + 0,bpl,0,buf->vb.height); + break; + case V4L2_FIELD_INTERLACED: + bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, + 0,bpl,bpl,buf->vb.height >> 1); + bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, + bpl,bpl,bpl,buf->vb.height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + bttv_risc_packed(btv,&buf->top,buf->vb.dma.sglist, + 0,bpl,0,buf->vb.height >> 1); + bttv_risc_packed(btv,&buf->bottom,buf->vb.dma.sglist, + bpf,bpl,0,buf->vb.height >> 1); + break; + default: + BUG(); + } + } + + /* planar modes */ + if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) { + int uoffset, voffset; + int ypadding, cpadding, lines; + + /* calculate chroma offsets */ + uoffset = buf->vb.width * buf->vb.height; + voffset = buf->vb.width * buf->vb.height; + if (buf->fmt->flags & FORMAT_FLAGS_CrCb) { + /* Y-Cr-Cb plane order */ + uoffset >>= buf->fmt->hshift; + uoffset >>= buf->fmt->vshift; + uoffset += voffset; + } else { + /* Y-Cb-Cr plane order */ + voffset >>= buf->fmt->hshift; + voffset >>= buf->fmt->vshift; + voffset += uoffset; + } + + switch (buf->vb.field) { + case V4L2_FIELD_TOP: + bttv_calc_geo(btv,&buf->geo,buf->vb.width, + buf->vb.height,0,buf->tvnorm); + bttv_risc_planar(btv, &buf->top, buf->vb.dma.sglist, + 0,buf->vb.width,0,buf->vb.height, + uoffset,voffset,buf->fmt->hshift, + buf->fmt->vshift,0); + break; + case V4L2_FIELD_BOTTOM: + bttv_calc_geo(btv,&buf->geo,buf->vb.width, + buf->vb.height,0,buf->tvnorm); + bttv_risc_planar(btv, &buf->bottom, buf->vb.dma.sglist, + 0,buf->vb.width,0,buf->vb.height, + uoffset,voffset,buf->fmt->hshift, + buf->fmt->vshift,0); + break; + case V4L2_FIELD_INTERLACED: + bttv_calc_geo(btv,&buf->geo,buf->vb.width, + buf->vb.height,1,buf->tvnorm); + lines = buf->vb.height >> 1; + ypadding = buf->vb.width; + cpadding = buf->vb.width >> buf->fmt->hshift; + bttv_risc_planar(btv,&buf->top, + buf->vb.dma.sglist, + 0,buf->vb.width,ypadding,lines, + uoffset,voffset, + buf->fmt->hshift, + buf->fmt->vshift, + cpadding); + bttv_risc_planar(btv,&buf->bottom, + buf->vb.dma.sglist, + ypadding,buf->vb.width,ypadding,lines, + uoffset+cpadding, + voffset+cpadding, + buf->fmt->hshift, + buf->fmt->vshift, + cpadding); + break; + case V4L2_FIELD_SEQ_TB: + bttv_calc_geo(btv,&buf->geo,buf->vb.width, + buf->vb.height,1,buf->tvnorm); + lines = buf->vb.height >> 1; + ypadding = buf->vb.width; + cpadding = buf->vb.width >> buf->fmt->hshift; + bttv_risc_planar(btv,&buf->top, + buf->vb.dma.sglist, + 0,buf->vb.width,0,lines, + uoffset >> 1, + voffset >> 1, + buf->fmt->hshift, + buf->fmt->vshift, + 0); + bttv_risc_planar(btv,&buf->bottom, + buf->vb.dma.sglist, + lines * ypadding,buf->vb.width,0,lines, + lines * ypadding + (uoffset >> 1), + lines * ypadding + (voffset >> 1), + buf->fmt->hshift, + buf->fmt->vshift, + 0); + break; + default: + BUG(); + } + } + + /* raw data */ + if (buf->fmt->flags & FORMAT_FLAGS_RAW) { + /* build risc code */ + buf->vb.field = V4L2_FIELD_SEQ_TB; + bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight, + 1,buf->tvnorm); + bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, + 0, RAW_BPL, 0, RAW_LINES); + bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, + buf->vb.size/2 , RAW_BPL, 0, RAW_LINES); + } + + /* copy format info */ + buf->btformat = buf->fmt->btformat; + buf->btswap = buf->fmt->btswap; + return 0; +} + +/* ---------------------------------------------------------- */ + +/* calculate geometry, build risc code */ +int +bttv_overlay_risc(struct bttv *btv, + struct bttv_overlay *ov, + const struct bttv_format *fmt, + struct bttv_buffer *buf) +{ + /* check interleave, bottom+top fields */ + dprintk(KERN_DEBUG + "bttv%d: overlay fields: %s format: %s size: %dx%d\n", + btv->c.nr, v4l2_field_names[buf->vb.field], + fmt->name,ov->w.width,ov->w.height); + + /* calculate geometry */ + bttv_calc_geo(btv,&buf->geo,ov->w.width,ov->w.height, + V4L2_FIELD_HAS_BOTH(ov->field), ov->tvnorm); + + /* build risc code */ + switch (ov->field) { + case V4L2_FIELD_TOP: + bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 0); + break; + case V4L2_FIELD_BOTTOM: + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 0); + break; + case V4L2_FIELD_INTERLACED: +#if 0 + bttv_risc_overlay(btv, &buf->top, fmt, ov, 1, 0); + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 0, 1); +#else + bttv_risc_overlay(btv, &buf->top, fmt, ov, 0, 1); + bttv_risc_overlay(btv, &buf->bottom, fmt, ov, 1, 0); +#endif + break; + default: + BUG(); + } + + /* copy format info */ + buf->btformat = fmt->btformat; + buf->btswap = fmt->btswap; + buf->vb.field = ov->field; + return 0; +} + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -up linux-2.4.26/drivers/media/video/bttv-vbi.c linux/drivers/media/video/bttv-vbi.c --- linux-2.4.26/drivers/media/video/bttv-vbi.c 2004-04-21 14:11:41.000000000 +0200 +++ linux/drivers/media/video/bttv-vbi.c 2004-04-21 14:11:41.000000000 +0200 @@ -0,0 +1,231 @@ +/* + bttv - Bt848 frame grabber driver + vbi interface + + (c) 2002 Gerd Knorr <kraxel@bytesex.org> + + 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/module.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/kdev_t.h> +#include <asm/io.h> +#include "bttvp.h" + +#define VBI_DEFLINES 16 +#define VBI_MAXLINES 32 + +static unsigned int vbibufs = 4; +static unsigned int vbi_debug = 0; + +MODULE_PARM(vbibufs,"i"); +MODULE_PARM_DESC(vbibufs,"number of vbi buffers, range 2-32, default 4"); +MODULE_PARM(vbi_debug,"i"); +MODULE_PARM_DESC(vbi_debug,"vbi code debug messages, default is 0 (no)"); + +#ifdef dprintk +# undef dprintk +#endif +#define dprintk(fmt, arg...) if (vbi_debug) \ + printk(KERN_DEBUG "bttv%d/vbi: " fmt, btv->c.nr , ## arg) + +/* ----------------------------------------------------------------------- */ +/* vbi risc code + mm */ + +static int +vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf, int lines) +{ + int bpl = 2048; + + bttv_risc_packed(btv, &buf->top, buf->vb.dma.sglist, + 0, bpl-4, 4, lines); + bttv_risc_packed(btv, &buf->bottom, buf->vb.dma.sglist, + lines * bpl, bpl-4, 4, lines); + return 0; +} + +static int vbi_buffer_setup(struct file *file, + unsigned int *count, unsigned int *size) +{ + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; + + if (0 == *count) + *count = vbibufs; + *size = fh->lines * 2 * 2048; + dprintk("setup: lines=%d\n",fh->lines); + return 0; +} + +static int vbi_buffer_prepare(struct file *file, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; + struct bttv_buffer *buf = (struct bttv_buffer*)vb; + int rc; + + buf->vb.size = fh->lines * 2 * 2048; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (STATE_NEEDS_INIT == buf->vb.state) { + if (0 != (rc = videobuf_iolock(btv->c.pci, &buf->vb, NULL))) + goto fail; + if (0 != (rc = vbi_buffer_risc(btv,buf,fh->lines))) + goto fail; + } + buf->vb.state = STATE_PREPARED; + buf->vb.field = field; + dprintk("buf prepare %p: top=%p bottom=%p field=%s\n", + vb, &buf->top, &buf->bottom, + v4l2_field_names[buf->vb.field]); + return 0; + + fail: + bttv_dma_free(btv,buf); + return rc; +} + +static void +vbi_buffer_queue(struct file *file, struct videobuf_buffer *vb) +{ + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; + struct bttv_buffer *buf = (struct bttv_buffer*)vb; + + dprintk("queue %p\n",vb); + buf->vb.state = STATE_QUEUED; + list_add_tail(&buf->vb.queue,&btv->vcapture); + if (NULL == btv->cvbi) { + fh->btv->curr.irqflags |= 4; + bttv_set_dma(btv,0x0c,fh->btv->curr.irqflags); + } +} + +static void vbi_buffer_release(struct file *file, struct videobuf_buffer *vb) +{ + struct bttv_fh *fh = file->private_data; + struct bttv *btv = fh->btv; + struct bttv_buffer *buf = (struct bttv_buffer*)vb; + + dprintk("free %p\n",vb); + bttv_dma_free(fh->btv,buf); +} + +struct videobuf_queue_ops bttv_vbi_qops = { + .buf_setup = vbi_buffer_setup, + .buf_prepare = vbi_buffer_prepare, + .buf_queue = vbi_buffer_queue, + .buf_release = vbi_buffer_release, +}; + +/* ----------------------------------------------------------------------- */ + +void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines) +{ + int vdelay; + + if (lines < 1) + lines = 1; + if (lines > VBI_MAXLINES) + lines = VBI_MAXLINES; + fh->lines = lines; + + vdelay = btread(BT848_E_VDELAY_LO); + if (vdelay < lines*2) { + vdelay = lines*2; + btwrite(vdelay,BT848_E_VDELAY_LO); + btwrite(vdelay,BT848_O_VDELAY_LO); + } +} + +void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f) +{ + const struct bttv_tvnorm *tvnorm; + u32 start0,start1; + s32 count0,count1,count; + + tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; + f->type = V4L2_BUF_TYPE_VBI_CAPTURE; + f->fmt.vbi.sampling_rate = tvnorm->Fsc; + f->fmt.vbi.samples_per_line = 2048; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 244; + f->fmt.vbi.flags = 0; + switch (fh->btv->tvnorm) { + case 1: /* NTSC */ + start0 = 10; + start1 = 273; + break; + case 0: /* PAL */ + case 2: /* SECAM */ + default: + start0 = 7; + start1 = 319; + } + + count0 = (f->fmt.vbi.start[0] + f->fmt.vbi.count[0]) - start0; + count1 = (f->fmt.vbi.start[1] + f->fmt.vbi.count[1]) - start1; + count = max(count0,count1); + if (count > VBI_MAXLINES) + count = VBI_MAXLINES; + if (count < 1) + count = 1; + + f->fmt.vbi.start[0] = start0; + f->fmt.vbi.start[1] = start1; + f->fmt.vbi.count[0] = count; + f->fmt.vbi.count[1] = count; +} + +void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f) +{ + const struct bttv_tvnorm *tvnorm; + + tvnorm = &bttv_tvnorms[fh->btv->tvnorm]; + memset(f,0,sizeof(*f)); + f->type = V4L2_BUF_TYPE_VBI_CAPTURE; + f->fmt.vbi.sampling_rate = tvnorm->Fsc; + f->fmt.vbi.samples_per_line = 2048; + f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; + f->fmt.vbi.offset = 244; + f->fmt.vbi.count[0] = fh->lines; + f->fmt.vbi.count[1] = fh->lines; + f->fmt.vbi.flags = 0; + switch (fh->btv->tvnorm) { + case 1: /* NTSC */ + f->fmt.vbi.start[0] = 10; + f->fmt.vbi.start[1] = 273; + break; + case 0: /* PAL */ + case 2: /* SECAM */ + default: + f->fmt.vbi.start[0] = 7; + f->fmt.vbi.start[1] = 319; + } +} + +/* ----------------------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff -up linux-2.4.26/drivers/media/video/bttv.h linux/drivers/media/video/bttv.h --- linux-2.4.26/drivers/media/video/bttv.h 2004-04-21 13:41:58.000000000 +0200 +++ linux/drivers/media/video/bttv.h 2004-04-21 14:11:41.000000000 +0200 @@ -14,6 +14,7 @@ #define _BTTV_H_ #include <linux/videodev.h> +#include <linux/i2c.h> /* ---------------------------------------------------------- */ /* exported by bttv-cards.c */ @@ -115,6 +116,14 @@ #define BTTV_XGUARD 0x67 #define BTTV_NEBULA_DIGITV 0x68 #define BTTV_PV143 0x69 +#define BTTV_IVC100 0x6e +#define BTTV_IVC120 0x6f +#define BTTV_PC_HDTV 0x70 +#define BTTV_TWINHAN_DST 0x71 +#define BTTV_WINFASTVC100 0x72 +#define BTTV_SIMUS_GVC1100 0x74 +#define BTTV_NGSTV_PLUS 0x75 +#define BTTV_LMLBT4 0x76 /* i2c address list */ #define I2C_TSA5522 0xc2 @@ -148,6 +157,18 @@ #define DIGITAL_MODE_VIDEO 1 #define DIGITAL_MODE_CAMERA 2 +struct bttv_core { + /* device structs */ + struct pci_dev *pci; + struct i2c_adapter i2c_adap; + struct list_head subs; /* struct bttv_sub_device */ + + /* device config */ + unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */ + unsigned int type; /* card type (pointer into tvcards[]) */ + char name[8]; /* dev name */ +}; + struct bttv; struct tvcard @@ -170,6 +191,12 @@ struct tvcard unsigned int needs_tvaudio:1; unsigned int msp34xx_alt:1; + /* flag: video pci function is unused */ + unsigned int no_video:1; + unsigned int has_dvb:1; + unsigned int has_remote:1; + unsigned int no_gpioirq:1; + /* other settings */ unsigned int pll; #define PLL_NONE 0 @@ -203,7 +230,9 @@ extern int bttv_handle_chipset(struct bt /* ---------------------------------------------------------- */ /* exported by bttv-if.c */ -/* interface for gpio access by other modules */ + +/* this obsolete -- please use the sysfs-based + interface below for new code */ /* returns card type + card ID (for bt878-based ones) for possible values see lines below beginning with #define BTTV_UNKNOWN @@ -250,8 +279,23 @@ extern wait_queue_head_t* bttv_get_gpio_ */ extern void bttv_i2c_call(unsigned int card, unsigned int cmd, void *arg); +/* with 2.6.x not needed thanks to the driver model + sysfs, see below */ +extern struct i2c_adapter *bttv_get_i2c_adap(unsigned int card); + + +/* ---------------------------------------------------------- */ +/* sysfs/driver-moded based gpio access interface */ + + +#define gpio_inout(mask,bits) btaor((mask)&(bits),~(mask),BT848_GPIO_OUT_EN) +#define gpio_read() btread(BT848_GPIO_DATA) +#define gpio_write(value) btwrite((value),BT848_GPIO_DATA) +#define gpio_bits(mask,bits) btaor((mask)&(bits),~(mask),BT848_GPIO_DATA) + + +/* ---------------------------------------------------------- */ +/* i2c */ -/* i2c */ extern void bttv_bit_setscl(void *data, int state); extern void bttv_bit_setsda(void *data, int state); extern void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg); --- linux-2.4.34/drivers/media/video/bttvp.h.orig 2006-12-23 21:34:20.000000000 +0100 +++ linux-2.4.34/drivers/media/video/bttvp.h 2007-02-19 11:51:35.448134000 +0100 @@ -1,11 +1,10 @@ /* bttv - Bt848 frame grabber driver - bttv's *private* header file -- nobody else than bttv itself + bttv's *private* header file -- nobody other than bttv itself should ever include this file. - Copyright (C) 1996,97 Ralph Metzler (rjkm@thp.uni-koeln.de) - (c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de> + (c) 2000-2002 Gerd Knorr <kraxel@bytesex.org> 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 @@ -26,131 +25,332 @@ #define _BTTVP_H_ #include <linux/version.h> -#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,108) +#define BTTV_VERSION_CODE KERNEL_VERSION(0,9,15) #include <linux/types.h> #include <linux/wait.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> +#include <linux/videodev.h> +#include <linux/pci.h> +#include <linux/input.h> +#include <asm/scatterlist.h> +#include <asm/io.h> -#include "bt848.h" -#include "bttv.h" +#include "video-buf.h" #include "audiochip.h" #include "tuner.h" #include "i2c-compat.h" +#include "ir-common.h" +#define strlcpy(dest,src,len) strncpy(dest,src,(len)-1) +#define iminor(inode) minor(inode->i_rdev) + +#include "bt848.h" +#include "bttv.h" +#include "btcx-risc.h" #ifdef __KERNEL__ +#define FORMAT_FLAGS_DITHER 0x01 +#define FORMAT_FLAGS_PACKED 0x02 +#define FORMAT_FLAGS_PLANAR 0x04 +#define FORMAT_FLAGS_RAW 0x08 +#define FORMAT_FLAGS_CrCb 0x10 + +#define RISC_SLOT_O_VBI 4 +#define RISC_SLOT_O_FIELD 6 +#define RISC_SLOT_E_VBI 10 +#define RISC_SLOT_E_FIELD 12 +#define RISC_SLOT_LOOP 14 + +#define RESOURCE_OVERLAY 1 +#define RESOURCE_VIDEO 2 +#define RESOURCE_VBI 4 + +#define RAW_LINES 640 +#define RAW_BPL 1024 + +#define UNSET (-1U) + +/* ---------------------------------------------------------- */ + +struct bttv_tvnorm { + int v4l2_id; + char *name; + u32 Fsc; + u16 swidth, sheight; /* scaled standard width, height */ + u16 totalwidth; + u8 adelay, bdelay, iform; + u32 scaledtwidth; + u16 hdelayx1, hactivex1; + u16 vdelay; + u8 vbipack; + u16 vtotal; + int sram; +}; +extern const struct bttv_tvnorm bttv_tvnorms[]; +extern const unsigned int BTTV_TVNORMS; + +struct bttv_format { + char *name; + int palette; /* video4linux 1 */ + int fourcc; /* video4linux 2 */ + int btformat; /* BT848_COLOR_FMT_* */ + int btswap; /* BT848_COLOR_CTL_* */ + int depth; /* bit/pixel */ + int flags; + int hshift,vshift; /* for planar modes */ +}; +extern const struct bttv_format bttv_formats[]; +extern const unsigned int BTTV_FORMATS; + +/* ---------------------------------------------------------- */ + +struct bttv_geometry { + u8 vtc,crop,comb; + u16 width,hscale,hdelay; + u16 sheight,vscale,vdelay,vtotal; +}; + +struct bttv_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* bttv specific */ + const struct bttv_format *fmt; + int tvnorm; + int btformat; + int btswap; + struct bttv_geometry geo; + struct btcx_riscmem top; + struct btcx_riscmem bottom; +}; + +struct bttv_buffer_set { + struct bttv_buffer *top; /* top field buffer */ + struct bttv_buffer *bottom; /* bottom field buffer */ + unsigned int irqflags; + unsigned int topirq; +}; + +struct bttv_overlay { + int tvnorm; + struct v4l2_rect w; + enum v4l2_field field; + struct v4l2_clip *clips; + int nclips; + int setup_ok; +}; + +struct bttv_fh { + struct bttv *btv; + int resources; +#ifdef VIDIOC_G_PRIORITY + enum v4l2_priority prio; +#endif + enum v4l2_buf_type type; + + /* video capture */ + struct videobuf_queue cap; + const struct bttv_format *fmt; + int width; + int height; + + /* current settings */ + const struct bttv_format *ovfmt; + struct bttv_overlay ov; + + /* video overlay */ + struct videobuf_queue vbi; + int lines; +}; + +/* ---------------------------------------------------------- */ +/* bttv-risc.c */ + +/* risc code generators - capture */ +int bttv_risc_packed(struct bttv *btv, struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int offset, unsigned int bpl, + unsigned int pitch, unsigned int lines); +int bttv_risc_planar(struct bttv *btv, struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int yoffset, unsigned int ybpl, + unsigned int ypadding, unsigned int ylines, + unsigned int uoffset, unsigned int voffset, + unsigned int hshift, unsigned int vshift, + unsigned int cpadding); +int bttv_risc_overlay(struct bttv *btv, struct btcx_riscmem *risc, + const struct bttv_format *fmt, + struct bttv_overlay *ov, + int skip_top, int skip_bottom); + +/* calculate / apply geometry settings */ +void bttv_calc_geo(struct bttv *btv, struct bttv_geometry *geo, + int width, int height, int interleaved, int norm); +void bttv_apply_geo(struct bttv *btv, struct bttv_geometry *geo, int top); + +/* control dma register + risc main loop */ +void bttv_set_dma(struct bttv *btv, int override, int irqflags); +int bttv_risc_init_main(struct bttv *btv); +int bttv_risc_hook(struct bttv *btv, int slot, struct btcx_riscmem *risc, + int irqflags); + +/* capture buffer handling */ +int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf); +int bttv_buffer_activate_video(struct bttv *btv, + struct bttv_buffer_set *set); +int bttv_buffer_activate_vbi(struct bttv *btv, + struct bttv_buffer *vbi); +void bttv_dma_free(struct bttv *btv, struct bttv_buffer *buf); + +/* overlay handling */ +int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov, + const struct bttv_format *fmt, + struct bttv_buffer *buf); + + +/* ---------------------------------------------------------- */ +/* bttv-vbi.c */ + +void bttv_vbi_try_fmt(struct bttv_fh *fh, struct v4l2_format *f); +void bttv_vbi_get_fmt(struct bttv_fh *fh, struct v4l2_format *f); +void bttv_vbi_setlines(struct bttv_fh *fh, struct bttv *btv, int lines); + +extern struct videobuf_queue_ops bttv_vbi_qops; + +/* ---------------------------------------------------------- */ +/* bttv-gpio.c */ + + /* ---------------------------------------------------------- */ /* bttv-driver.c */ -/* insmod options / kernel args */ -extern unsigned int no_overlay; +/* insmod options */ extern unsigned int bttv_verbose; extern unsigned int bttv_debug; extern unsigned int bttv_gpio; extern void bttv_gpio_tracking(struct bttv *btv, char *comment); extern int init_bttv_i2c(struct bttv *btv); +extern int fini_bttv_i2c(struct bttv *btv); extern int pvr_boot(struct bttv *btv); -#define dprintk if (bttv_debug) printk -#define vprintk if (bttv_verbose) printk +extern int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg); +extern void bttv_reinit_bt848(struct bttv *btv); +extern void bttv_field_count(struct bttv *btv); + +#define vprintk if (bttv_verbose) printk +#define dprintk if (bttv_debug >= 1) printk +#define d2printk if (bttv_debug >= 2) printk +/* our devices */ #define BTTV_MAX 16 -extern unsigned int bttv_num; /* number of Bt848s in use */ - -#define UNSET -1U +extern unsigned int bttv_num; -#ifdef VIDEODAT_HACK -# define VBI_MAXLINES 19 -#else -# define VBI_MAXLINES 16 -#endif +#define BTTV_MAX_FBUF 0x208000 #define VBIBUF_SIZE (2048*VBI_MAXLINES*2) -#define MAX_GBUFFERS 64 -#define RISCMEM_LEN (32744*2) +#define BTTV_TIMEOUT (HZ/2) /* 0.5 seconds */ +#define BTTV_FREE_IDLE (HZ) /* one second */ -#define BTTV_MAX_FBUF 0x208000 - -struct bttv_window -{ - int x, y; - ushort width, height; - ushort bpp, bpl; - ushort swidth, sheight; - unsigned long vidadr; - ushort freq; - int norm; - int interlace; - int color_fmt; - ushort depth; -}; struct bttv_pll_info { - unsigned int pll_ifreq; /* PLL input frequency */ - unsigned int pll_ofreq; /* PLL output frequency */ + unsigned int pll_ifreq; /* PLL input frequency */ + unsigned int pll_ofreq; /* PLL output frequency */ unsigned int pll_crystal; /* Crystal used for input */ unsigned int pll_current; /* Currently programmed ofreq */ }; -struct bttv_gbuf { - int stat; -#define GBUFFER_UNUSED 0 -#define GBUFFER_GRABBING 1 -#define GBUFFER_DONE 2 -#define GBUFFER_ERROR 3 - struct timeval tv; - - u16 width; - u16 height; - u16 fmt; - - u32 *risc; - unsigned long ro; - unsigned long re; +/* for gpio-connected remote control */ +struct bttv_input { + struct input_dev dev; + struct ir_input_state ir; + char name[32]; + char phys[32]; + u32 mask_keycode; + u32 mask_keydown; +}; + +struct bttv_suspend_state { + u32 pci_cfg[64 / sizeof(u32)]; + u32 gpio_enable; + u32 gpio_data; + int disabled; + struct bttv_buffer_set video; + struct bttv_buffer *vbi; }; struct bttv { - struct video_device video_dev; - struct video_device radio_dev; - struct video_device vbi_dev; - struct video_picture picture; /* Current picture params */ - struct video_audio audio_dev; /* Current audio params */ + struct bttv_core c; - spinlock_t s_lock; - struct semaphore lock; - unsigned int user; + /* pci device config */ + unsigned short id; + unsigned char revision; + unsigned char *bt848_mmio; /* pointer to mmio */ - /* i2c */ - struct i2c_adapter i2c_adap; + /* card configuration info */ + unsigned int cardid; /* pci subsystem id (bt878 based ones) */ + unsigned int tuner_type; /* tuner chip type */ + unsigned int pinnacle_id; + unsigned int svhs; + struct bttv_pll_info pll; + int triton1; + int gpioirq; + int use_i2c_hw; + + /* old gpio interface */ + wait_queue_head_t gpioq; + int shutdown; + void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set); + + /* new gpio interface */ + spinlock_t gpio_lock; + + /* i2c layer */ struct i2c_algo_bit_data i2c_algo; struct i2c_client i2c_client; int i2c_state, i2c_rc; + int i2c_done; + wait_queue_head_t i2c_queue; - unsigned int tuner_type; - unsigned int pinnacle_id; - unsigned int channel; - unsigned int svhs; - - unsigned int nr; - unsigned short id; - struct pci_dev *dev; - unsigned char revision; - unsigned long bt848_adr; /* bus address of IO mem returned by PCI BIOS */ - unsigned char *bt848_mem; /* pointer to mapped IO memory */ - unsigned long busriscmem; - u32 *riscmem; - - unsigned char *vbibuf; - struct bttv_window win; - int fb_color_ctl; - int type; /* card type */ - unsigned int cardid; - int audio; /* audio mode */ - int audio_chip; /* set to one of the chips supported by bttv.c */ - int radio; - int has_radio; + /* video4linux (1) */ + struct video_device *video_dev; + struct video_device *radio_dev; + struct video_device *vbi_dev; + + /* infrared remote */ int has_remote; + struct bttv_input *remote; + + /* locking */ + spinlock_t s_lock; + struct semaphore lock; + int resources; + struct semaphore reslock; +#ifdef VIDIOC_G_PRIORITY + struct v4l2_prio_state prio; +#endif + + /* video state */ + unsigned int input; + unsigned int audio; + unsigned long freq; + int tvnorm,hue,contrast,bright,saturation; + struct v4l2_framebuffer fbuf; + unsigned int field_count; + + /* various options */ + int opt_combfilter; + int opt_lumafilter; + int opt_automute; + int opt_chroma_agc; + int opt_adc_crush; + int opt_vcr_hack; + int opt_whitecrush_upper; + int opt_whitecrush_lower; + + /* radio data/state */ + int has_radio; + int radio_user; /* miro/pinnacle + Aimslab VHX philips matchbox (tea5757 radio tuner) support */ @@ -166,70 +366,46 @@ int mbox_iow; int mbox_csel; - u32 *risc_jmp; - u32 *vbi_odd; - u32 *vbi_even; - u32 bus_vbi_even; - u32 bus_vbi_odd; - wait_queue_head_t vbiq; - wait_queue_head_t capq; - unsigned int vbip; - - u32 *risc_scr_odd; - u32 *risc_scr_even; - u32 risc_cap_odd; - u32 risc_cap_even; - int scr_on; - int vbi_on; - struct video_clip *cliprecs; - - struct bttv_gbuf *gbuf; - int gqueue[MAX_GBUFFERS]; - int gq_in,gq_out,gq_grab,gq_start; - char *fbuffer; + /* risc memory management data + - must aquire s_lock before changing these + - only the irq handler is supported to touch top + bottom + vcurr */ + struct btcx_riscmem main; + struct bttv_buffer *screen; /* overlay */ + struct list_head capture; /* video capture queue */ + struct list_head vcapture; /* vbi capture queue */ + struct bttv_buffer_set curr; /* active buffers */ + struct bttv_buffer *cvbi; /* active vbi buffer */ + int new_input; + + unsigned long cap_ctl; + unsigned long dma_on; + struct timer_list timeout; + struct bttv_suspend_state state; + + /* stats */ + unsigned int errors; + unsigned int framedrop; + unsigned int irq_total; + unsigned int irq_me; - struct bttv_pll_info pll; - unsigned int Fsc; - unsigned int field; - unsigned int last_field; /* number of last grabbed field */ - int i2c_command; - int triton1; - - int errors; - int needs_restart; - - wait_queue_head_t gpioq; - int shutdown; - void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set); + unsigned int users; + struct bttv_fh init; }; +/* private ioctls */ +#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int) +#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int) + extern struct bttv bttvs[BTTV_MAX]; #endif -#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mem+(adr))) -#define btread(adr) readl(btv->bt848_mem+(adr)) +#define btwrite(dat,adr) writel((dat), (char *) (btv->bt848_mmio+(adr))) +#define btread(adr) readl(btv->bt848_mmio+(adr)) #define btand(dat,adr) btwrite((dat) & btread(adr), adr) #define btor(dat,adr) btwrite((dat) | btread(adr), adr) #define btaor(dat,mask,adr) btwrite((dat) | ((mask) & btread(adr)), adr) -/* bttv ioctls */ - -#define BTTV_READEE _IOW('v', BASE_VIDIOCPRIVATE+0, char [256]) -#define BTTV_WRITEE _IOR('v', BASE_VIDIOCPRIVATE+1, char [256]) -#define BTTV_FIELDNR _IOR('v' , BASE_VIDIOCPRIVATE+2, unsigned int) -#define BTTV_PLLSET _IOW('v' , BASE_VIDIOCPRIVATE+3, struct bttv_pll_info) -#define BTTV_BURST_ON _IOR('v' , BASE_VIDIOCPRIVATE+4, int) -#define BTTV_BURST_OFF _IOR('v' , BASE_VIDIOCPRIVATE+5, int) -#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int) -#define BTTV_PICNR _IOR('v' , BASE_VIDIOCPRIVATE+7, int) -#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int) - -#define TDA9850 0x01 -#define TDA9840 0x02 -#define TDA8425 0x03 -#define TEA6300 0x04 - #endif /* _BTTVP_H_ */ /*