Commit 12eb9056 authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] v4l: major bttv update

This is a major update of the bttv core (0.7.x to 0.8.x).  There are way
too many changes to list them all, the complete core code for video
frame capture has been rewritten from scratch.
parent fbc37a8b
......@@ -28,7 +28,8 @@ O_TARGET := video.o
export-objs := i2c-old.o videodev.o bttv-if.o cpia.o video-buf.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
zoran-objs := zr36120.o zr36120_i2c.o zr36120_mem.o
obj-$(CONFIG_VIDEO_DEV) += videodev.o
......
......@@ -39,9 +39,9 @@
#include "tuner.h"
/* fwd decl */
static void boot_msp34xx(struct bttv *btv, int pin);
static void hauppauge_eeprom(struct bttv *btv);
static void avermedia_eeprom(struct bttv *btv);
static void init_PXC200(struct bttv *btv);
#if 0
static void init_tea5757(struct bttv *btv);
......@@ -55,6 +55,9 @@ 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 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);
static void windvr_audio(struct bttv *btv, struct video_audio *v, int set);
static void rv605_muxsel(struct bttv *btv, unsigned int input);
/* config variables */
static int triton1=0;
......@@ -77,7 +80,8 @@ MODULE_PARM(triton1,"i");
MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
"[enable bug compatibility for triton1 + others]");
MODULE_PARM(vsfx,"i");
MODULE_PARM_DESC(vsfx,"set VSFX pci config bit [yet another chipset flaw workaround]");
MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
"[yet another chipset flaw workaround]");
MODULE_PARM(no_overlay,"i");
MODULE_PARM(card,"1-4i");
MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
......@@ -151,7 +155,7 @@ static struct CARD {
{ 0x00041461, BTTV_AVERMEDIA98, "AVerMedia TVCapture 98" },
{ 0x300014ff, BTTV_MAGICTVIEW061, "TView 99 (CPH061)" },
{ 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master" },
{ 0x300214ff, BTTV_PHOEBE_TVMAS, "Phoebe TV Master (CPH060)" },
{ 0x1117153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x1118153b, BTTV_TERRATVALUE, "Terratec TValue" },
......@@ -161,6 +165,7 @@ static struct CARD {
{ 0x1127153b, BTTV_TERRATV, "Terratec TV+" },
{ 0x1134153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" },
{ 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x400a15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV" },
{ 0x400d15b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
......@@ -168,12 +173,14 @@ static struct CARD {
{ 0x401615b0, BTTV_ZOLTRIX_GENIE, "Zoltrix Genie TV / Radio" },
{ 0x010115cb, BTTV_GMV1, "AG GMV1" },
{ 0x010114c7, BTTV_MODTEC_205, "Modular Technology PCTV" },
{ 0x010114c7, BTTV_MODTEC_205, "Modular Technology MM205 PCTV" },
{ 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" },
{ 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" },
{ 0x217d6606, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" },
{ 0x03116000, BTTV_SENSORAY311, "Sensoray 311" },
{ 0x00790e11, BTTV_WINDVR, "Canopus WinDVR PCI" },
{ 0, -1, NULL }
};
......@@ -276,8 +283,8 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x08 ---------------------------------- */
name: "Fly Video II (Bt848)",
video_inputs: 3,
name: "FlyVideo II (Bt848) LR26",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
svhs: 2,
......@@ -285,9 +292,10 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0xc00, 0x800, 0x400, 0xc00, 0},
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
},{
name: "TurboTV",
name: "IXMicro TurboTV",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -346,14 +354,14 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: -1,
},{
name: "Aimslab VHX",
name: "Aimslab Video Highway Xtreme (VHX)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
svhs: 2,
gpiomask: 7,
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 1, 2, 3, 4},
audiomux: { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */
needs_tvaudio: 1,
tuner_type: -1,
},{
......@@ -393,6 +401,7 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1,
tuner_type: -1,
audio_hook: winview_audio,
has_radio: 1,
},{
name: "AVEC Intercapture",
video_inputs: 3,
......@@ -438,7 +447,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: TUNER_PHILIPS_PAL_I,
},{
name: "Phoebe Tv Master + FM",
name: "Phoebe Tv Master + FM (CPH050)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -752,6 +761,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x1f0000,
muxsel: { 2, 3, 1, 1},
audiomux: { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff },
needs_tvaudio: 1,
no_msp34xx: 1,
pll: PLL_35,
tuner_type: 1,
......@@ -788,7 +798,7 @@ struct tvcard bttv_tvcards[] = {
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
svhs: 2,
svhs: 3,
gpiomask: 0xAA0000,
muxsel: { 2,3,1,1 },
audiomux: { 0x20000, 0, 0x80000, 0x80000, 0xa8000, 0x46000 },
......@@ -860,7 +870,7 @@ struct tvcard bttv_tvcards[] = {
},{
/* Miguel Angel Alvarez <maacruz@navegalia.com>
old Easy TV BT848 version (model CPH031) */
name: "BESTBUY Easy TV",
name: "BESTBUY Easy TV (CPH031)",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -890,7 +900,7 @@ struct tvcard bttv_tvcards[] = {
/* This is the ultimate cheapo capture card
* just a BT848A on a small PCB!
* Steve Hosgood <steve@equiinet.com> */
name: "GrandTec 'Grand Video Capture'",
name: "GrandTec 'Grand Video Capture' (Bt848)",
video_inputs: 2,
audio_inputs: 0,
tuner: -1,
......@@ -904,7 +914,7 @@ struct tvcard bttv_tvcards[] = {
tuner_type: -1,
},{
/* Daniel Herrington <daniel.herrington@home.com> */
name: "Phoebe TV Master Only (No FM)",
name: "Phoebe TV Master Only (No FM) CPH060",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -917,7 +927,7 @@ struct tvcard bttv_tvcards[] = {
tuner_type: TUNER_TEMIC_4036FY5_NTSC,
},{
/* Matti Mottus <mottus@physic.ut.ee> */
name: "TV Capturer",
name: "TV Capturer (CPH03X)",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -931,7 +941,7 @@ struct tvcard bttv_tvcards[] = {
/* ---- card 0x3c ---------------------------------- */
/* Philip Blundell <philb@gnu.org> */
name: "MM100PCTV",
name: "Modular Technology MM100PCTV",
video_inputs: 2,
audio_inputs: 2,
gpiomask: 11,
......@@ -977,7 +987,7 @@ struct tvcard bttv_tvcards[] = {
tuner: 0,
svhs: 2,
gpiomask: 0xf03f,
muxsel: { 2, 3, 0, 1},
muxsel: { 2, 3, 1, 0 },
audiomux: { 0xbffe, 0, 0xbfff, 0, 0xbffe},
pll: PLL_28,
tuner_type: TUNER_TEMIC_4006FN5_MULTI_PAL,
......@@ -1005,7 +1015,13 @@ struct tvcard bttv_tvcards[] = {
svhs: 2,
gpiomask: 0x18e0,
muxsel: { 2, 3, 0, 1},
audiomux: { 0,0x18e0,0x1000,0x1000,0x1080, 0x1080 },
/* Radio changed from 1e80 to 0x800 to make
Flyvideo2000S in .hu happy (gm)*/
/* -dk-???: set mute=0x1800 for tda9874h daughterboard */
audiomux: { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 },
audio_hook: fv2000s_audio,
no_msp34xx: 1,
no_tda9875: 1,
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: 5,
......@@ -1047,11 +1063,12 @@ struct tvcard bttv_tvcards[] = {
tuner: 0,
svhs: -1,
gpiomask: 0x4f8a00,
// 0x100000: 1=MSP enabled (0=disable again)
// 0x010000: somehow influences tuner picture quality (?)
audiomux: {0x947fff, 0x987fff,0x947fff,0x947fff},
//tvtuner, radio, external,internal,mute,stereo
muxsel: { 2, 3 ,0 ,1}, /* tuner, Composit, SVid, Composit-on-Svid-adapter*/
// 0x100000: 1=MSP enabled (0=disable again)
// 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC)
audiomux: {0x947fff, 0x987fff,0x947fff,0x947fff, 0x947fff},
// tvtuner, radio, external,internal, mute, stereo
/* tuner, Composit, SVid, Composit-on-Svid-adapter*/
muxsel: { 2, 3 ,0 ,1},
tuner_type: TUNER_MT2032,
pll: PLL_28,
has_radio: 1,
......@@ -1106,8 +1123,74 @@ struct tvcard bttv_tvcards[] = {
tuner_type: -1,
audio_hook: pvbt878p9b_audio,
has_radio: 1,
}
};
},{
/* Clay Kunz <ckunz@mail.arc.nasa.gov> */
/* you must jumper JP5 for the card to work */
name: "Sensoray 311",
video_inputs: 5,
audio_inputs: 0,
tuner: -1,
svhs: 4,
gpiomask: 0,
muxsel: { 2, 3, 1, 0, 0},
audiomux: { 0 },
needs_tvaudio: 0,
tuner_type: -1,
},{
/* Miguel Freitas <miguel@cetuc.puc-rio.br> */
name: "RemoteVision MX (RV605)",
video_inputs: 16,
audio_inputs: 0,
tuner: 0,
svhs: 0,
gpiomask: 0x00,
gpiomask2: 0x07ff,
muxsel: { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03,
0xd3, 0xb3, 0xc3, 0x63, 0x93, 0x53, 0x83, 0xa3 },
no_msp34xx: 1,
no_tda9875: 1,
tuner_type: -1,
muxsel_hook: rv605_muxsel,
},{
name: "Powercolor MTV878/ MTV878R/ MTV878F",
video_inputs: 3,
audio_inputs: 2,
svhs: 2,
gpiomask: 0x1C800F, // Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset
muxsel: { 2, 1, 1, },
audiomux: { 0, 1, 2, 2, 4 },
needs_tvaudio: 0,
tuner_type: TUNER_PHILIPS_PAL,
pll: PLL_28,
has_radio: 1,
},{
/* ---- card 0x4c ---------------------------------- */
/* Masaki Suzuki <masaki@btree.org> */
name: "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
svhs: 2,
gpiomask: 0x140007,
muxsel: { 2, 3, 1, 1 },
audiomux: { 0, 1, 2, 3, 4, 0 },
tuner_type: TUNER_PHILIPS_NTSC,
audio_hook: windvr_audio,
},{
name: "GrandTec Multi Capture Card (Bt878)",
video_inputs: 4,
audio_inputs: 0,
tuner: -1,
svhs: -1,
gpiomask: 0,
muxsel: { 2, 3, 1, 0 },
audiomux: { 0 },
needs_tvaudio: 0,
no_msp34xx: 1,
pll: PLL_28,
tuner_type: -1,
}};
const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard));
......@@ -1195,12 +1278,58 @@ void __devinit bttv_idcard(struct bttv *btv)
* (most) board specific initialisations goes here
*/
static void flyvideo_gpio(struct bttv *btv)
{
int gpio,outbits;
int tuner=-1,ttype;
outbits = btread(BT848_GPIO_OUT_EN);
btwrite(0x00, BT848_GPIO_OUT_EN);
udelay(8); // without this we would see the 0x1800 mask
gpio=btread(BT848_GPIO_DATA);
btwrite(outbits, BT848_GPIO_OUT_EN);
// all cards provide GPIO info, some have an additional eeprom
// lowest 3 bytes are remote control codes (no handshake needed)
ttype=(gpio&0x0f0000)>>16;
switch(ttype) {
case 0: tuner=4; // None
break;
case 4: tuner=5; // Philips PAL
break;
case 6: tuner=37; // LG PAL (newer TAPC series)
break;
case 0xC: tuner=3; // Philips SECAM(+PAL)
break;
default:
printk(KERN_INFO "bttv%d: flyvideo_gpio: unknown tuner type.\n", btv->nr);
}
printk(KERN_INFO "bttv%d: Flyvideo Radio=%s RemoteControl=%s Tuner=%d gpio=0x%06x\n",
btv->nr,
gpio&0x400000? "yes":"no",
gpio&0x800000? "yes":"no", tuner, gpio);
btv->tuner_type = tuner;
btv->has_radio = gpio&0x400000? 1:0;
}
int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
14,2,17,1, 4,1,4,3, 1,2,16,1, 4,4,4,4 };
int miro_fmtuner[] = { 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1,
1,1,1,1, 1,1,1,0, 0,0,0,0, 0,0,0,0 };
void __devinit bttv_init_card(struct bttv *btv)
/* initialization part one -- before registering i2c bus */
void __devinit bttv_init_card1(struct bttv *btv)
{
if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
boot_msp34xx(btv,5);
if (btv->type == BTTV_VOODOOTV_FM)
boot_msp34xx(btv,20);
}
/* initialization part one -- after registering i2c bus */
void __devinit bttv_init_card2(struct bttv *btv)
{
/* miro/pinnacle */
if (btv->type == BTTV_MIRO ||
......@@ -1246,6 +1375,15 @@ void __devinit bttv_init_card(struct bttv *btv)
#endif
}
if (btv->type == BTTV_FLYVIDEO_98 ||
btv->type == BTTV_FLYVIDEO ||
btv->type == BTTV_TYPHOON_TVIEW ||
btv->type == BTTV_CHRONOS_VS2 ||
btv->type == BTTV_FLYVIDEO_98FM ||
btv->type == BTTV_FLYVIDEO2000 ||
btv->type == BTTV_FLYVIDEO98EZ)
flyvideo_gpio(btv);
if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) {
/* pick up some config infos from the eeprom */
bttv_readee(btv,eeprom_data,0xa0);
......@@ -1449,22 +1587,24 @@ static void __devinit hauppauge_eeprom(struct bttv *btv)
btv->tuner_type, radio ? "yes" : "no");
}
// AVermedia specific stuff...
// from bktr_card.c
/* ----------------------------------------------------------------------- */
/* AVermedia specific stuff, from bktr_card.c */
int tuner_0_table[] = {
TUNER_PHILIPS_NTSC, TUNER_PHILIPS_PAL,
TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL,
TUNER_PHILIPS_PAL, TUNER_PHILIPS_PAL,
TUNER_PHILIPS_SECAM, TUNER_PHILIPS_SECAM,
TUNER_PHILIPS_SECAM, TUNER_PHILIPS_PAL};
/*
#if 0
int tuner_0_fm_table[] = {
PHILIPS_FR1236_NTSC, PHILIPS_FR1216_PAL,
PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL,
PHILIPS_FR1216_PAL, PHILIPS_FR1216_PAL,
PHILIPS_FR1236_SECAM, PHILIPS_FR1236_SECAM,
PHILIPS_FR1236_SECAM, PHILIPS_FR1216_PAL};
*/
#endif
int tuner_1_table[] = {
TUNER_TEMIC_NTSC, TUNER_TEMIC_PAL,
......@@ -1475,11 +1615,12 @@ int tuner_1_table[] = {
static void __devinit avermedia_eeprom(struct bttv *btv)
{
int tuner_make,tuner_tv_fm,tuner_format,tuner=0;
int tuner_make,tuner_tv_fm,tuner_format,tuner=0,remote;
tuner_make = (eeprom_data[0x41] & 0x7);
tuner_tv_fm = (eeprom_data[0x41] & 0x18) >> 3;
tuner_format = (eeprom_data[0x42] & 0xf0) >> 4;
remote = (eeprom_data[0x42] & 0x01);
if (tuner_make == 0 || tuner_make == 2)
if(tuner_format <=9)
......@@ -1492,11 +1633,32 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
btv->nr,eeprom_data[0x41],eeprom_data[0x42]);
if(tuner) {
btv->tuner_type=tuner;
printk("%d\n",tuner);
printk("%d",tuner);
} else
printk("Unknown type\n");
printk("Unknown type");
printk(" radio:%s remote control:%s\n",
tuner_tv_fm?"yes":"no",
remote?"yes":"no");
}
/* used on Voodoo TV/FM (Voodoo 200), S0 wired to 0x10000 */
void bttv_tda9880_setnorm(struct bttv *btv, int norm)
{
// fix up our card entry
if(norm==VIDEO_MODE_NTSC) {
bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[0]=0x957fff;
bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[4]=0x957fff;
dprintk("bttv_tda9880_setnorm to NTSC\n");
}
else {
bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[0]=0x947fff;
bttv_tvcards[BTTV_VOODOOTV_FM].audiomux[4]=0x947fff;
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);
}
/*
......@@ -1506,7 +1668,7 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
* Hauppauge: pin 5
* Voodoo: pin 20
*/
void __devinit bttv_boot_msp34xx(struct bttv *btv, int pin)
static void __devinit boot_msp34xx(struct bttv *btv, int pin)
{
int mask = (1 << pin);
......@@ -1918,10 +2080,116 @@ pvbt878p9b_audio(struct bttv *btv, struct video_audio *v, int set)
}
} else {
v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
}
}
/*
* Dariusz Kowalewski <darekk@automex.pl>
* sound control for FlyVideo 2000S (with tda9874 decoder)
* based on pvbt878p9b_audio() - this is not tested, please fix!!!
*/
static void
fv2000s_audio(struct bttv *btv, struct video_audio *v, int set)
{
unsigned int val = 0xffff;
#if BTTV_VERSION_CODE > KERNEL_VERSION(0,8,0)
if (btv->radio_user)
return;
#else
if (btv->radio)
return;
#endif
if (set) {
if (v->mode & VIDEO_SOUND_MONO) {
val = 0x0000;
}
if ((v->mode & (VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2))
|| (v->mode & VIDEO_SOUND_STEREO)) {
val = 0x1080; //-dk-???: 0x0880, 0x0080, 0x1800 ...
}
if (val != 0xffff) {
btaor(val, ~0x1800, BT848_GPIO_DATA);
if (bttv_gpio)
bttv_gpio_tracking(btv,"fv2000s");
}
} else {
v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
}
}
/*
* sound control for Canopus WinDVR PCI
* Masaki Suzuki <masaki@btree.org>
*/
static void
windvr_audio(struct bttv *btv, struct video_audio *v, int set)
{
unsigned long val = 0;
if (set) {
if (v->mode & VIDEO_SOUND_MONO)
val = 0x040000;
if (v->mode & VIDEO_SOUND_LANG1)
val = 0;
if (v->mode & VIDEO_SOUND_LANG2)
val = 0x100000;
if (v->mode & VIDEO_SOUND_STEREO)
val = 0;
if (val) {
btaor(val, ~0x140000, BT848_GPIO_DATA);
if (bttv_gpio)
bttv_gpio_tracking(btv,"windvr");
}
} else {
v->mode = VIDEO_SOUND_MONO | VIDEO_SOUND_STEREO |
VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2;
}
}
/* RemoteVision MX (rv605) muxsel helper [Miguel Freitas]
*
* This is needed because rv605 don't use a normal multiplex, but a crosspoint
* switch instead (CD22M3494E). This IC can have multiple active connections
* between Xn (input) and Yn (output) pins. We need to clear any existing
* connection prior to establish a new one, pulsing the STROBE pin.
*
* The board hardwire Y0 (xpoint) to MUX1 and MUXOUT to Yin.
* GPIO pins are wired as:
* GPIO[0:3] - AX[0:3] (xpoint) - P1[0:3] (microcontroler)
* GPIO[4:6] - AY[0:2] (xpoint) - P1[4:6] (microcontroler)
* GPIO[7] - DATA (xpoint) - P1[7] (microcontroler)
* GPIO[8] - - P3[5] (microcontroler)
* GPIO[9] - RESET (xpoint) - P3[6] (microcontroler)
* GPIO[10] - STROBE (xpoint) - P3[7] (microcontroler)
* GPINTR - - P3[4] (microcontroler)
*
* The microcontroler is a 80C32 like. It should be possible to change xpoint
* configuration either directly (as we are doing) or using the microcontroler
* which is also wired to I2C interface. I have no further info on the
* microcontroler features, one would need to disassembly the firmware.
* note: the vendor refused to give any information on this product, all
* that stuff was found using a multimeter! :)
*/
static void rv605_muxsel(struct bttv *btv, unsigned int input)
{
/* reset all conections */
btaor(0x200,~0x200, BT848_GPIO_DATA);
mdelay(1);
btaor(0x000,~0x200, BT848_GPIO_DATA);
mdelay(1);
/* create a new conection */
btaor(0x080,~0x480, BT848_GPIO_DATA);
btaor(0x480,~0x480, BT848_GPIO_DATA);
mdelay(1);
btaor(0x080,~0x480, BT848_GPIO_DATA);
mdelay(1);
}
/* ----------------------------------------------------------------------- */
/* motherboard chipset specific stuff */
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
bttv-risc.c -- interfaces to other kernel modules
bttv risc code handling
- memory management
- generation
(c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#define __NO_VERSION__ 1
#include <linux/version.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/iobuf.h>
#include <linux/vmalloc.h>
#include <linux/interrupt.h>
#include <asm/page.h>
#include <asm/pgtable.h>
#include "bttvp.h"
/* ---------------------------------------------------------- */
/* allocate/free risc memory */
int bttv_riscmem_alloc(struct pci_dev *pci,
struct bttv_riscmem *risc,
unsigned int size)
{
unsigned long *cpu;
dma_addr_t dma;
cpu = pci_alloc_consistent(pci, size, &dma);
if (NULL == cpu)
return -ENOMEM;
memset(cpu,0,size);
if (risc->cpu && risc->size < size) {
/* realloc (enlarge buffer) -- copy old stuff */
memcpy(cpu,risc->cpu,risc->size);
bttv_riscmem_free(pci,risc);
}
risc->cpu = cpu;
risc->dma = dma;
risc->size = size;
return 0;
}
void bttv_riscmem_free(struct pci_dev *pci,
struct bttv_riscmem *risc)
{
if (NULL == risc->cpu)
return;
pci_free_consistent(pci, risc->size, risc->cpu, risc->dma);
memset(risc,0,sizeof(*risc));
}
/* ---------------------------------------------------------- */
/* risc code generators */
int
bttv_risc_packed(struct bttv *btv, struct bttv_riscmem *risc,
struct scatterlist *sglist,
int offset, int bpl, int padding, int lines)
{
int instructions,rc,line,todo;
struct scatterlist *sg;
unsigned long *rp;
/* 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 = bttv_riscmem_alloc(btv->dev,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++) {
while (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;
}
/* save pointer to jmp instruction address */
risc->jmp = rp;
return 0;
}
int
bttv_risc_planar(struct bttv *btv, struct bttv_riscmem *risc,
struct scatterlist *sglist,
int yoffset, int ybpl, int ypadding, int ylines,
int uoffset, int voffset, int hshift, int vshift,
int cpadding)
{
int instructions,rc,line,todo,ylen,chroma;
unsigned long *rp,ri;
struct scatterlist *ysg;
struct scatterlist *usg;
struct scatterlist *vsg;
/* 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 = bttv_riscmem_alloc(btv->dev,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++) {
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 >= sg_dma_len(ysg)) {
yoffset -= sg_dma_len(ysg);
ysg++;
}
while (uoffset >= sg_dma_len(usg)) {
uoffset -= sg_dma_len(usg);
usg++;
}
while (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;
}
/* ---------------------------------------------------------- */
struct SKIPLIST {
int start;
int end;
};
int
bttv_screen_clips(struct video_buffer *fbuf,
int x, int y, int width, int height,
struct video_clip *clips, int n)
{
if (x < 0) {
/* left */
clips[n].x = 0;
clips[n].y = 0;
clips[n].width = -x;
clips[n].height = height;
n++;
}
if (x+width > fbuf->width) {
/* right */
clips[n].x = fbuf->width - x;
clips[n].y = 0;
clips[n].width = width - clips[n].x;
clips[n].height = height;
n++;
}
if (y < 0) {
/* top */
clips[n].x = 0;
clips[n].y = 0;
clips[n].width = width;
clips[n].height = -y;
n++;
}
if (y+height > fbuf->height) {
/* bottom */
clips[n].x = 0;
clips[n].y = fbuf->height - y;
clips[n].width = width;
clips[n].height = height - clips[n].y;
n++;
}
return n;
}
void
bttv_sort_clips(struct video_clip *clips, int nclips)
{
struct video_clip swap;
int i,j,n;
for (i = nclips-2; i >= 0; i--) {
for (n = 0, j = 0; j <= i; j++) {
if (clips[j].x > clips[j+1].x) {
swap = clips[j];
clips[j] = clips[j+1];
clips[j+1] = swap;
n++;
}
}
if (0 == n)
break;
}
}
static void
calc_skips(int line, int width, int *maxy,
struct SKIPLIST *skips, int *nskips,
const struct video_clip *clips, int nclips)
{
int clip,skip,maxline,end;
skip=0;
maxline = 9999;
for (clip = 0; clip < nclips; clip++) {
/* sanity checks */
if (clips[clip].x + clips[clip].width <= 0)
continue;
if (clips[clip].x > width)
break;
/* vertical range */
if (line > clips[clip].y+clips[clip].height-1)
continue;
if (line < clips[clip].y) {
if (maxline > clips[clip].y-1)
maxline = clips[clip].y-1;
continue;
}
if (maxline > clips[clip].y+clips[clip].height-1)
maxline = clips[clip].y+clips[clip].height-1;
/* horizontal range */
if (0 == skip || clips[clip].x > skips[skip-1].end) {
/* new one */
skips[skip].start = clips[clip].x;
if (skips[skip].start < 0)
skips[skip].start = 0;
skips[skip].end = clips[clip].x + clips[clip].width;
if (skips[skip].end > width)
skips[skip].end = width;
skip++;
} else {
/* overlaps -- expand last one */
end = clips[clip].x + clips[clip].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 (bttv_debug) {
printk(KERN_DEBUG "bttv: skips line %d-%d:",line,maxline);
for (skip = 0; skip < *nskips; skip++) {
printk(" %d-%d",skips[skip].start,skips[skip].end);
}
printk("\n");
}
}
int
bttv_risc_overlay(struct bttv *btv, struct bttv_riscmem *risc,
const struct bttv_format *fmt, struct bttv_overlay *ov,
int fields)
{
int instructions,rc,line,maxy,start,end,skip,nskips;
struct SKIPLIST *skips;
unsigned long *rp,ri,ra;
unsigned long 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) *
((fields & VBUF_FIELD_INTER) ? ov->height>>1 : ov->height);
instructions += 2;
if ((rc = bttv_riscmem_alloc(btv->dev,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);
addr = (unsigned long)btv->fbuf.base;
addr += btv->fbuf.bytesperline * ov->y;
addr += ((btv->fbuf.depth+7) >> 3) * ov->x;
/* scan lines */
for (maxy = -1, line = 0; line < ov->height;
line++, addr += btv->fbuf.bytesperline) {
if (fields & VBUF_FIELD_INTER) {
if ((line%2) != 0 && (fields & VBUF_FIELD_ODD))
continue;
if ((line%2) != 1 && (fields & VBUF_FIELD_EVEN))
continue;
}
/* calculate clipping */
if (line > maxy)
calc_skips(line, ov->width, &maxy,
skips, &nskips, ov->clips, ov->nclips);
/* write out risc code */
for (start = 0, skip = 0; start < ov->width; start = end) {
if (skip >= nskips) {
ri = BT848_RISC_WRITE;
end = ov->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->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;
u32 xsf, sr;
int vdelay;
tvnorm = &bttv_tvnorms[norm];
vdelay = tvnorm->vdelay;
if (vdelay < btv->vbi.lines*2)
vdelay = btv->vbi.lines*2;
xsf = (width*tvnorm->scaledtwidth)/tvnorm->swidth;
geo->hscale = ((tvnorm->totalwidth*4096UL)/xsf-4096);
geo->hdelay = tvnorm->hdelayx1;
geo->hdelay = (geo->hdelay*width)/tvnorm->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;
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);
}
/* ---------------------------------------------------------- */
/* 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->odd) btv->cap_ctl |= 0x02;
if (NULL != btv->even) btv->cap_ctl |= 0x01;
if (NULL != btv->vcurr) 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 odd=%08Lx/%08Lx even=%08Lx/%08Lx\n",
btv->nr,capctl,irqflags,
btv->vcurr ? (u64)btv->vcurr->odd.dma : 0,
btv->odd ? (u64)btv->odd->odd.dma : 0,
btv->vcurr ? (u64)btv->vcurr->even.dma : 0,
btv->even ? (u64)btv->even->even.dma : 0);
cmd = BT848_RISC_JUMP;
if (irqflags) {
cmd |= BT848_RISC_IRQ | (irqflags << 16);
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 = bttv_riscmem_alloc(btv->dev,&btv->main,PAGE_SIZE)) < 0)
return rc;
dprintk(KERN_DEBUG "bttv%d: risc main @ %08Lx\n",
btv->nr,(u64)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));
/* odd 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);
/* even 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 odd 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 bttv_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->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->nr,risc,slot,(u64)risc->dma,irqflags);
cmd = BT848_RISC_JUMP;
if (irqflags)
cmd |= BT848_RISC_IRQ | (irqflags << 16);
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->dev, &buf->vb.dma);
videobuf_dma_free(&buf->vb.dma);
bttv_riscmem_free(btv->dev,&buf->even);
bttv_riscmem_free(btv->dev,&buf->odd);
buf->vb.state = STATE_NEEDS_INIT;
}
int
bttv_buffer_activate(struct bttv *btv,
struct bttv_buffer *odd,
struct bttv_buffer *even)
{
if (NULL != odd && NULL != even) {
odd->vb.state = STATE_ACTIVE;
even->vb.state = STATE_ACTIVE;
bttv_apply_geo(btv, &odd->geo, 1);
bttv_apply_geo(btv, &even->geo,0);
bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &odd->odd, 0);
bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &even->even, 0);
btaor((odd->btformat & 0xf0) | (even->btformat & 0x0f),
~0xff, BT848_COLOR_FMT);
btaor((odd->btswap & 0x0a) | (even->btswap & 0x05),
~0x0f, BT848_COLOR_CTL);
} else if (NULL != odd) {
odd->vb.state = STATE_ACTIVE;
bttv_apply_geo(btv, &odd->geo,1);
bttv_apply_geo(btv, &odd->geo,0);
bttv_risc_hook(btv, RISC_SLOT_O_FIELD, &odd->odd, 0);
bttv_risc_hook(btv, RISC_SLOT_E_FIELD, NULL, 0);
btaor(odd->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
btaor(odd->btswap & 0x0f, ~0x0f, BT848_COLOR_CTL);
} else if (NULL != even) {
even->vb.state = STATE_ACTIVE;
bttv_apply_geo(btv, &even->geo,1);
bttv_apply_geo(btv, &even->geo,0);
bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0);
bttv_risc_hook(btv, RISC_SLOT_E_FIELD, &even->even, 0);
btaor(even->btformat & 0xff, ~0xff, BT848_COLOR_FMT);
btaor(even->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;
}
/* ---------------------------------------------------------- */
int
bttv_buffer_field(struct bttv *btv, int field, int def_field,
int norm, int height)
{
const struct bttv_tvnorm *tvnorm = bttv_tvnorms + norm;
/* check interleave, even+odd fields */
if (height > (tvnorm->sheight >> 1)) {
field = VBUF_FIELD_ODD | VBUF_FIELD_EVEN | VBUF_FIELD_INTER;
} else {
if (field & def_field) {
field = def_field;
} else {
field = (def_field & VBUF_FIELD_EVEN)
? VBUF_FIELD_ODD : VBUF_FIELD_EVEN;
}
}
return field;
}
/* 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;
buf->vb.field = bttv_buffer_field(btv,buf->vb.field, VBUF_FIELD_EVEN,
buf->tvnorm,buf->vb.height);
dprintk(KERN_DEBUG
"bttv%d: buffer flags:%s%s%s format: %s size: %dx%d\n",
btv->nr,
(buf->vb.field & VBUF_FIELD_INTER) ? " interleave" : "",
(buf->vb.field & VBUF_FIELD_ODD) ? " odd" : "",
(buf->vb.field & VBUF_FIELD_EVEN) ? " even" : "",
buf->fmt->name,buf->vb.width,buf->vb.height);
/* packed pixel modes */
if (buf->fmt->flags & FORMAT_FLAGS_PACKED) {
int bpl, padding, lines;
bpl = (buf->fmt->depth >> 3) * buf->vb.width;
/* calculate geometry */
if (buf->vb.field & VBUF_FIELD_INTER) {
bttv_calc_geo(btv,&buf->geo,buf->vb.width,
buf->vb.height,1,buf->tvnorm);
lines = buf->vb.height >> 1;
padding = bpl;
} else {
bttv_calc_geo(btv,&buf->geo,buf->vb.width,
buf->vb.height,0,buf->tvnorm);
lines = buf->vb.height;
padding = 0;
}
/* build risc code */
if (buf->vb.field & VBUF_FIELD_ODD)
bttv_risc_packed(btv,&buf->odd,buf->vb.dma.sglist,
0,bpl,padding,lines);
if (buf->vb.field & VBUF_FIELD_EVEN)
bttv_risc_packed(btv,&buf->even,buf->vb.dma.sglist,
padding,bpl,padding,lines);
}
/* planar modes */
if (buf->fmt->flags & FORMAT_FLAGS_PLANAR) {
struct bttv_riscmem *risc;
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;
}
if (buf->vb.field & VBUF_FIELD_INTER) {
bttv_calc_geo(btv,&buf->geo,buf->vb.width,
buf->vb.height,1,buf->tvnorm);
lines = buf->vb.height >> 1;
ypadding = buf->vb.width;
if (!buf->fmt->vshift) {
/* chroma planes are interleaved too */
cpadding = buf->vb.width >> buf->fmt->hshift;
} else {
cpadding = 0;
}
bttv_risc_planar(btv,&buf->odd,
buf->vb.dma.sglist,
0,buf->vb.width,ypadding,lines,
uoffset,voffset,buf->fmt->hshift,
buf->fmt->vshift >> 1,
cpadding);
bttv_risc_planar(btv,&buf->even,
buf->vb.dma.sglist,
ypadding,buf->vb.width,ypadding,lines,
uoffset+cpadding,
voffset+cpadding,
buf->fmt->hshift,
cpadding ? (buf->fmt->vshift>>1) : -1,
cpadding);
} else {
if (buf->vb.field & VBUF_FIELD_ODD)
risc = &buf->odd;
else
risc = &buf->even;
bttv_calc_geo(btv,&buf->geo,buf->vb.width,
buf->vb.height,0,buf->tvnorm);
bttv_risc_planar(btv, risc, buf->vb.dma.sglist,
0,buf->vb.width,0,buf->vb.height,
uoffset,voffset,buf->fmt->hshift,
buf->fmt->vshift,0);
}
}
/* raw data */
if (buf->fmt->flags & FORMAT_FLAGS_RAW) {
/* build risc code */
buf->vb.field = VBUF_FIELD_INTER | VBUF_FIELD_ODD | VBUF_FIELD_EVEN;
bttv_calc_geo(btv,&buf->geo,tvnorm->swidth,tvnorm->sheight,
1,buf->tvnorm);
bttv_risc_packed(btv, &buf->odd, buf->vb.dma.sglist,
0, RAW_BPL, 0, RAW_LINES);
bttv_risc_packed(btv, &buf->even, 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)
{
int interleave;
/* check interleave, even+odd fields */
buf->vb.field = bttv_buffer_field(btv, 0, VBUF_FIELD_ODD,
ov->tvnorm,ov->height);
dprintk(KERN_DEBUG
"bttv%d: overlay flags:%s%s%s format: %s size: %dx%d\n",
btv->nr,
(buf->vb.field & VBUF_FIELD_INTER) ? " interleave" : "",
(buf->vb.field & VBUF_FIELD_ODD) ? " odd" : "",
(buf->vb.field & VBUF_FIELD_EVEN) ? " even" : "",
fmt->name,ov->width,ov->height);
/* calculate geometry */
interleave = buf->vb.field & VBUF_FIELD_INTER;
bttv_calc_geo(btv,&buf->geo,ov->width,ov->height,
interleave, ov->tvnorm);
/* build risc code */
if (buf->vb.field & VBUF_FIELD_ODD)
bttv_risc_overlay(btv, &buf->odd, fmt, ov,
interleave | VBUF_FIELD_ODD);
if (buf->vb.field & VBUF_FIELD_EVEN)
bttv_risc_overlay(btv, &buf->even,fmt, ov,
interleave | VBUF_FIELD_EVEN);
/* copy format info */
buf->btformat = fmt->btformat;
buf->btswap = fmt->btswap;
return 0;
}
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
/*
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/version.h>
#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->nr, ## arg)
#ifndef HAVE_V4L2
/* some dummy defines to avoid cluttering up the source code with
a huge number of ifdef's for V4L2 */
# define V4L2_BUF_TYPE_CAPTURE -1
# define V4L2_BUF_TYPE_VBI -1
#endif
/* ----------------------------------------------------------------------- */
/* vbi risc code + mm */
static int
vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
{
int bpl = 2048;
bttv_risc_packed(btv, &buf->odd, buf->vb.dma.sglist,
0, bpl-4, 4, btv->vbi.lines);
bttv_risc_packed(btv, &buf->even, buf->vb.dma.sglist,
btv->vbi.lines * bpl, bpl-4, 4, btv->vbi.lines);
return 0;
}
static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
{
int rc;
buf->vb.size = btv->vbi.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->dev,&buf->vb)))
goto fail;
if (0 != (rc = vbi_buffer_risc(btv,buf)))
goto fail;
}
buf->vb.state = STATE_PREPARED;
dprintk("buf prepare ok: odd=%p even=%p\n",&buf->odd,&buf->even);
return 0;
fail:
bttv_dma_free(btv,buf);
return rc;
}
static void
vbi_buffer_queue(struct bttv *btv, struct bttv_buffer *buf)
{
unsigned long flags;
buf->vb.state = STATE_QUEUED;
spin_lock_irqsave(&btv->s_lock,flags);
list_add_tail(&buf->vb.queue,&btv->vcapture);
bttv_set_dma(btv,0x0c,1);
spin_unlock_irqrestore(&btv->s_lock,flags);
}
static void vbi_buffer_release(struct file *file, struct videobuf_buffer *vb)
{
struct bttv *btv = file->private_data;
struct bttv_buffer *buf = (struct bttv_buffer*)vb;
bttv_dma_free(btv,buf);
}
static void
vbi_cancel_all(struct bttv *btv)
{
unsigned long flags;
int i;
/* remove queued buffers from list */
spin_lock_irqsave(&btv->s_lock,flags);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == btv->vbi.bufs[i])
continue;
if (btv->vbi.bufs[i]->vb.state == STATE_QUEUED) {
list_del(&btv->vbi.bufs[i]->vb.queue);
btv->vbi.bufs[i]->vb.state = STATE_ERROR;
}
}
spin_unlock_irqrestore(&btv->s_lock,flags);
/* free all buffers + clear queue */
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == btv->vbi.bufs[i])
continue;
bttv_dma_free(btv,btv->vbi.bufs[i]);
}
INIT_LIST_HEAD(&btv->vbi.stream);
}
/* ----------------------------------------------------------------------- */
static int vbi_read_start(struct file *file, struct bttv *btv)
{
int err,size,count,i;
if (vbibufs < 2 || vbibufs > VIDEO_MAX_FRAME)
vbibufs = 2;
count = vbibufs;
size = btv->vbi.lines * 2 * 2048;
err = videobuf_mmap_setup(file,
(struct videobuf_buffer**)btv->vbi.bufs,
sizeof(struct bttv_buffer),
count,size,V4L2_BUF_TYPE_VBI,
vbi_buffer_release);
if (err)
return err;
for (i = 0; i < count; i++) {
err = vbi_buffer_prepare(btv,btv->vbi.bufs[i]);
if (err)
return err;
list_add_tail(&btv->vbi.bufs[i]->vb.stream,&btv->vbi.stream);
vbi_buffer_queue(btv,btv->vbi.bufs[i]);
}
btv->vbi.reading = 1;
return 0;
}
static void vbi_read_stop(struct bttv *btv)
{
int i;
vbi_cancel_all(btv);
INIT_LIST_HEAD(&btv->vbi.stream);
for (i = 0; i < vbibufs; i++) {
kfree(btv->vbi.bufs[i]);
btv->vbi.bufs[i] = NULL;
}
btv->vbi.reading = 0;
}
static void vbi_setlines(struct bttv *btv, int lines)
{
int vdelay;
if (lines < 1)
lines = 1;
if (lines > VBI_MAXLINES)
lines = VBI_MAXLINES;
btv->vbi.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);
}
}
#ifdef HAVE_V4L2
static void vbi_fmt(struct bttv *btv, struct v4l2_format *f)
{
memset(f,0,sizeof(*f));
f->type = V4L2_BUF_TYPE_VBI;
f->fmt.vbi.sampling_rate = 35468950;
f->fmt.vbi.samples_per_line = 2048;
f->fmt.vbi.sample_format = V4L2_VBI_SF_UBYTE;
f->fmt.vbi.offset = 244;
f->fmt.vbi.count[0] = btv->vbi.lines;
f->fmt.vbi.count[1] = btv->vbi.lines;
f->fmt.vbi.flags = 0;
switch (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;
}
}
#endif
/* ----------------------------------------------------------------------- */
/* vbi interface */
static int vbi_open(struct inode *inode, struct file *file)
{
unsigned int minor = minor(inode->i_rdev);
struct bttv *btv = NULL;
int i;
for (i = 0; i < bttv_num; i++) {
if (bttvs[i].vbi_dev.minor == minor) {
btv = &bttvs[i];
break;
}
}
if (NULL == btv)
return -ENODEV;
down(&btv->vbi.lock);
if (btv->vbi.users) {
up(&btv->vbi.lock);
return -EBUSY;
}
dprintk("open minor=%d\n",minor);
file->private_data = btv;
btv->vbi.users++;
bttv_field_count(btv);
vbi_setlines(btv,VBI_DEFLINES);
up(&btv->vbi.lock);
return 0;
}
static int vbi_release(struct inode *inode, struct file *file)
{
struct bttv *btv = file->private_data;
down(&btv->vbi.lock);
if (btv->vbi.reading) {
vbi_read_stop(btv);
btv->vbi.read_buf = NULL;
}
btv->vbi.users--;
bttv_field_count(btv);
vbi_setlines(btv,0);
up(&btv->vbi.lock);
return 0;
}
static int vbi_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
struct bttv *btv = file->private_data;
#ifdef HAVE_V4L2
unsigned long flags;
int err;
#endif
if (btv->errors)
bttv_reinit_bt848(btv);
switch (cmd) {
case VIDIOCGCAP:
{
struct video_capability *cap = arg;
memset(cap,0,sizeof(*cap));
strcpy(cap->name,btv->vbi_dev.name);
cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT;
return 0;
}
/* vbi/teletext ioctls */
case BTTV_VBISIZE:
return btv->vbi.lines * 2 * 2048;
case BTTV_VERSION:
case VIDIOCGFREQ:
case VIDIOCSFREQ:
case VIDIOCGTUNER:
case VIDIOCSTUNER:
case VIDIOCGCHAN:
case VIDIOCSCHAN:
return bttv_common_ioctls(btv,cmd,arg);
#ifdef HAVE_V4L2
case VIDIOC_QUERYCAP:
{
struct v4l2_capability *cap = arg;
memset(cap,0,sizeof(*cap));
strcpy(cap->name, btv->name);
cap->type = V4L2_TYPE_VBI;
cap->flags = V4L2_FLAG_TUNER | V4L2_FLAG_READ |
V4L2_FLAG_STREAMING | V4L2_FLAG_SELECT;
return 0;
}
case VIDIOC_G_FMT:
{
struct v4l2_format *f = arg;
vbi_fmt(btv,f);
return 0;
}
case VIDIOC_S_FMT:
{
struct v4l2_format *f = arg;
if (btv->vbi.reading || btv->vbi.streaming)
return -EBUSY;
vbi_setlines(btv,f->fmt.vbi.count[0]);
vbi_fmt(btv,f);
return 0;
}
case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers *req = arg;
int size,count;
if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
return -EINVAL;
if (req->count < 1)
return -EINVAL;
down(&btv->vbi.lock);
err = -EINVAL;
size = btv->vbi.lines * 2 * 2048;
size = (size + PAGE_SIZE - 1) & PAGE_MASK;
count = req->count;
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
err = videobuf_mmap_setup(file,
(struct videobuf_buffer**)btv->vbi.bufs,
sizeof(struct bttv_buffer),
count,size,V4L2_BUF_TYPE_CAPTURE,
vbi_buffer_release);
if (err < 0)
goto fh_unlock_and_return;
req->type = V4L2_BUF_TYPE_VBI;
req->count = count;
up(&btv->vbi.lock);
return 0;
}
case VIDIOC_QUERYBUF:
{
struct v4l2_buffer *b = arg;
if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
return -EINVAL;
if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
return -EINVAL;
if (NULL == btv->vbi.bufs[b->index])
return -EINVAL;
videobuf_status(b,&btv->vbi.bufs[b->index]->vb);
return 0;
}
case VIDIOC_QBUF:
{
struct v4l2_buffer *b = arg;
struct bttv_buffer *buf;
if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
return -EINVAL;
if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
return -EINVAL;
down(&btv->vbi.lock);
err = -EINVAL;
buf = btv->vbi.bufs[b->index];
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;
err = vbi_buffer_prepare(btv,buf);
if (0 != err)
goto fh_unlock_and_return;
list_add_tail(&buf->vb.stream,&btv->vbi.stream);
if (btv->vbi.streaming)
vbi_buffer_queue(btv,buf);
up(&btv->vbi.lock);
return 0;
}
case VIDIOC_DQBUF:
{
struct v4l2_buffer *b = arg;
struct bttv_buffer *buf;
if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_VBI)
return -EINVAL;
down(&btv->vbi.lock);
err = -EINVAL;
if (list_empty(&btv->vbi.stream))
goto fh_unlock_and_return;
buf = list_entry(btv->vbi.stream.next,
struct bttv_buffer, vb.stream);
err = videobuf_waiton(&buf->vb,0,1);
if (err < 0)
goto fh_unlock_and_return;
switch (buf->vb.state) {
case STATE_ERROR:
err = -EIO;
/* fall through */
case STATE_DONE:
videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
buf->vb.state = STATE_IDLE;
break;
default:
err = -EINVAL;
goto fh_unlock_and_return;
}
list_del(&buf->vb.stream);
memset(b,0,sizeof(*b));
videobuf_status(b,&buf->vb);
up(&btv->vbi.lock);
return err;
}
case VIDIOC_STREAMON:
{
struct list_head *list;
struct bttv_buffer *buf;
down(&btv->vbi.lock);
err = -EBUSY;
if (btv->vbi.reading)
goto fh_unlock_and_return;
spin_lock_irqsave(&btv->s_lock,flags);
list_for_each(list,&btv->vbi.stream) {
buf = list_entry(list, struct bttv_buffer, vb.stream);
if (buf->vb.state == STATE_PREPARED)
vbi_buffer_queue(btv,buf);
}
spin_unlock_irqrestore(&btv->s_lock,flags);
btv->vbi.streaming = 1;
up(&btv->vbi.lock);
return 0;
}
case VIDIOC_STREAMOFF:
{
down(&btv->vbi.lock);
err = -EINVAL;
if (!btv->vbi.streaming)
goto fh_unlock_and_return;
vbi_cancel_all(btv);
INIT_LIST_HEAD(&btv->vbi.stream);
btv->vbi.streaming = 0;
up(&btv->vbi.lock);
return 0;
}
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_FREQ:
case VIDIOC_S_FREQ:
return bttv_common_ioctls(btv,cmd,arg);
#endif /* HAVE_V4L2 */
default:
return -ENOIOCTLCMD;
}
return 0;
#ifdef HAVE_V4L2
fh_unlock_and_return:
up(&btv->vbi.lock);
return err;
#endif
}
static ssize_t vbi_read(struct file *file, char *data,
size_t count, loff_t *ppos)
{
unsigned int *fc;
struct bttv *btv = file->private_data;
int err, bytes, retval = 0;
if (btv->errors)
bttv_reinit_bt848(btv);
down(&btv->vbi.lock);
if (!btv->vbi.reading) {
retval = vbi_read_start(file,btv);
if (retval < 0)
goto done;
}
while (count > 0) {
/* get / wait for data */
if (NULL == btv->vbi.read_buf) {
btv->vbi.read_buf = list_entry(btv->vbi.stream.next,
struct bttv_buffer,
vb.stream);
list_del(&btv->vbi.read_buf->vb.stream);
btv->vbi.read_off = 0;
}
err = videobuf_waiton(&btv->vbi.read_buf->vb,
file->f_flags & O_NONBLOCK,1);
if (err < 0) {
if (0 == retval)
retval = err;
break;
}
#if 1
/* dirty, undocumented hack -- pass the frame counter
* within the last four bytes of each vbi data block.
* We need that one to maintain backward compatibility
* to all vbi decoding software out there ... */
fc = (unsigned int*)btv->vbi.read_buf->vb.dma.vmalloc;
fc += (btv->vbi.read_buf->vb.size>>2) -1;
*fc = btv->vbi.read_buf->vb.field_count >> 1;
#endif
/* copy stuff */
bytes = count;
if (bytes > btv->vbi.read_buf->vb.size - btv->vbi.read_off)
bytes = btv->vbi.read_buf->vb.size - btv->vbi.read_off;
if (copy_to_user(data + retval,btv->vbi.read_buf->vb.dma.vmalloc +
btv->vbi.read_off,bytes)) {
if (0 == retval)
retval = -EFAULT;
break;
}
count -= bytes;
retval += bytes;
btv->vbi.read_off += bytes;
dprintk("read: %d bytes\n",bytes);
/* requeue buffer when done with copying */
if (btv->vbi.read_off == btv->vbi.read_buf->vb.size) {
list_add_tail(&btv->vbi.read_buf->vb.stream,
&btv->vbi.stream);
vbi_buffer_queue(btv,btv->vbi.read_buf);
btv->vbi.read_buf = NULL;
}
}
done:
up(&btv->vbi.lock);
return retval;
}
static unsigned int vbi_poll(struct file *file, poll_table *wait)
{
struct bttv *btv = file->private_data;
struct bttv_buffer *buf = NULL;
unsigned int rc = 0;
down(&btv->vbi.lock);
if (btv->vbi.streaming) {
if (!list_empty(&btv->vbi.stream))
buf = list_entry(btv->vbi.stream.next,
struct bttv_buffer, vb.stream);
} else {
if (!btv->vbi.reading)
vbi_read_start(file,btv);
if (!btv->vbi.reading) {
rc = POLLERR;
} else if (NULL == btv->vbi.read_buf) {
btv->vbi.read_buf = list_entry(btv->vbi.stream.next,
struct bttv_buffer,
vb.stream);
list_del(&btv->vbi.read_buf->vb.stream);
btv->vbi.read_off = 0;
}
buf = btv->vbi.read_buf;
}
if (!buf)
rc = POLLERR;
if (0 == rc) {
poll_wait(file, &buf->vb.done, wait);
if (buf->vb.state == STATE_DONE ||
buf->vb.state == STATE_ERROR)
rc = POLLIN|POLLRDNORM;
}
up(&btv->vbi.lock);
return rc;
}
static int
vbi_mmap(struct file *file, struct vm_area_struct * vma)
{
struct bttv *btv = file->private_data;
int err;
down(&btv->vbi.lock);
err = videobuf_mmap_mapper
(vma,(struct videobuf_buffer**)btv->vbi.bufs);
up(&btv->vbi.lock);
return err;
}
static struct file_operations vbi_fops =
{
owner: THIS_MODULE,
open: vbi_open,
release: vbi_release,
ioctl: video_generic_ioctl,
llseek: no_llseek,
read: vbi_read,
poll: vbi_poll,
mmap: vbi_mmap,
};
struct video_device bttv_vbi_template =
{
name: "bt848/878 vbi",
type: VID_TYPE_TUNER|VID_TYPE_TELETEXT,
hardware: VID_HARDWARE_BT848,
fops: &vbi_fops,
kernel_ioctl: vbi_ioctl,
minor: -1,
};
/*
* Local variables:
* c-basic-offset: 8
* End:
*/
......@@ -87,7 +87,9 @@
#define BTTV_PV_BT878P_PLUS 0x46
#define BTTV_FLYVIDEO98EZ 0x47
#define BTTV_PV_BT878P_9B 0x48
#define BTTV_SENSORAY311 0x49
#define BTTV_RV605 0x4a
#define BTTV_WINDVR 0x4c
/* i2c address list */
#define I2C_TSA5522 0xc2
......@@ -95,7 +97,7 @@
#define I2C_TDA8425 0x82
#define I2C_TDA9840 0x84
#define I2C_TDA9850 0xb6 /* also used by 9855,9873 */
#define I2C_TDA9874A 0xb0 /* also used by 9875 */
#define I2C_TDA9874 0xb0 /* also used by 9875 */
#define I2C_TDA9875 0xb0
#define I2C_HAUPEE 0xa0
#define I2C_STBEE 0xae
......@@ -123,7 +125,7 @@ struct tvcard
int tuner;
int svhs;
u32 gpiomask;
u32 muxsel[8];
u32 muxsel[16];
u32 audiomux[6]; /* Tuner, Radio, external, internal, mute, stereo */
u32 gpiomask2; /* GPIO MUX mask */
......@@ -141,6 +143,7 @@ struct tvcard
int tuner_type;
int has_radio;
void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
void (*muxsel_hook)(struct bttv *btv, unsigned int input);
};
extern struct tvcard bttv_tvcards[];
......@@ -148,11 +151,12 @@ extern const int bttv_num_tvcards;
/* identification / initialization of the card */
extern void bttv_idcard(struct bttv *btv);
extern void bttv_init_card(struct bttv *btv);
extern void bttv_init_card1(struct bttv *btv);
extern void bttv_init_card2(struct bttv *btv);
/* card-specific funtions */
extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
extern void bttv_boot_msp34xx(struct bttv *btv, int pin);
extern void bttv_tda9880_setnorm(struct bttv *btv, int norm);
/* kernel cmd line parse helper */
extern int bttv_parse(char *str, int max, int *vals);
......
/*
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
......@@ -25,204 +24,339 @@
#ifndef _BTTVP_H_
#define _BTTVP_H_
#define BTTV_VERSION_CODE KERNEL_VERSION(0,7,83)
#define BTTV_VERSION_CODE KERNEL_VERSION(0,8,38)
#include <linux/types.h>
#include <linux/wait.h>
#include <linux/i2c.h>
#include <linux/i2c-algo-bit.h>
#include <linux/videodev.h>
#include <linux/iobuf.h>
#include <linux/pci.h>
#include <asm/scatterlist.h>
#include "bt848.h"
#include "bttv.h"
#include "video-buf.h"
#include "audiochip.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_STREAMING 2
#define RAW_LINES 640
#define RAW_BPL 1024
/* ---------------------------------------------------------- */
struct bttv_tvnorm
{
int v4l2_id;
u32 Fsc;
u16 swidth, sheight; /* scaled standard width, height */
u16 totalwidth;
u8 adelay, bdelay, iform;
u32 scaledtwidth;
u16 hdelayx1, hactivex1;
u16 vdelay;
u8 vbipack;
};
extern const struct bttv_tvnorm bttv_tvnorms[];
extern const 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 int BTTV_FORMATS;
/* ---------------------------------------------------------- */
struct bttv_geometry {
u8 vtc,crop,comb;
u16 width,hscale,hdelay;
u16 sheight,vscale,vdelay;
};
struct bttv_riscmem {
unsigned int size;
unsigned long *cpu;
unsigned long *jmp;
dma_addr_t dma;
};
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 bttv_riscmem even;
struct bttv_riscmem odd;
};
struct bttv_overlay {
int tvnorm;
int x,y,width,height;
struct video_clip *clips;
int nclips;
};
struct bttv_vbi {
struct semaphore lock;
int users;
int lines;
/* mmap */
int streaming;
struct bttv_buffer *bufs[VIDEO_MAX_FRAME];
struct list_head stream;
/* read */
int reading;
int read_off;
struct bttv_buffer *read_buf;
};
struct bttv_fh {
struct bttv *btv;
/* locking */
int resources;
struct semaphore lock;
/* keep current driver settings */
const struct bttv_format *ovfmt;
struct bttv_overlay ov;
struct bttv_buffer buf;
/* for read() capture */
struct bttv_buffer read_buf;
int read_off;
/* mmap()'ed buffers */
struct bttv_buffer *bufs[VIDEO_MAX_FRAME];
struct list_head stream; /* v4l2 QBUF/DQBUF */
};
/* ---------------------------------------------------------- */
/* bttv-risc.c */
/* alloc/free memory */
int bttv_riscmem_alloc(struct pci_dev *pci,
struct bttv_riscmem *risc,
unsigned int size);
void bttv_riscmem_free(struct pci_dev *pci,
struct bttv_riscmem *risc);
/* risc code generators - capture */
int bttv_risc_packed(struct bttv *btv, struct bttv_riscmem *risc,
struct scatterlist *sglist,
int offset, int bpl, int pitch, int lines);
int bttv_risc_planar(struct bttv *btv, struct bttv_riscmem *risc,
struct scatterlist *sglist,
int yoffset, int ybpl, int ypadding, int ylines,
int uoffset, int voffset, int hshift, int vshift,
int cpadding);
/* risc code generator + helpers - screen overlay */
int bttv_screen_clips(struct video_buffer *fbuf,
int x, int y, int width, int height,
struct video_clip *clips, int n);
void bttv_sort_clips(struct video_clip *clips, int nclips);
int bttv_risc_overlay(struct bttv *btv, struct bttv_riscmem *risc,
const struct bttv_format *fmt,
struct bttv_overlay *ov, int flags);
/* 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 odd);
/* 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 bttv_riscmem *risc,
int irqflags);
/* capture buffer handling */
int bttv_buffer_field(struct bttv *btv, int field, int def_field,
int tvnorm, int height);
int bttv_buffer_risc(struct bttv *btv, struct bttv_buffer *buf);
int bttv_buffer_activate(struct bttv *btv, struct bttv_buffer *odd,
struct bttv_buffer *even);
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 */
extern struct video_device bttv_vbi_template;
/* ---------------------------------------------------------- */
/* bttv-driver.c */
/* insmod options / kernel args */
extern 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);
#define dprintk if (bttv_debug) 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
/* Anybody who uses more than four? */
/* our devices */
#define BTTV_MAX 4
extern int bttv_num; /* number of Bt848s in use */
extern int bttv_num;
extern struct bttv bttvs[BTTV_MAX];
#ifndef O_NONCAP
#define O_NONCAP O_TRUNC
#endif
#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;
};
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 */
/* pci device config */
struct pci_dev *dev;
unsigned short id;
unsigned char revision;
unsigned char *bt848_mmio; /* pointer to mmio */
/* card configuration info */
unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */
char name[8]; /* dev name */
int cardid; /* pci subsystem id (bt878 based ones) */
int type; /* card type (pointer into tvcards[]) */
int tuner_type; /* tuner chip type */
struct bttv_pll_info pll;
int triton1;
spinlock_t s_lock;
struct semaphore lock;
int user;
int capuser;
/* gpio interface */
wait_queue_head_t gpioq;
int shutdown;
/* i2c */
/* i2c layer */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
struct i2c_client i2c_client;
int i2c_state, i2c_rc;
struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
int tuner_type;
int channel;
unsigned int nr;
unsigned short id;
struct pci_dev *dev;
unsigned int irq; /* IRQ used by Bt848 card */
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 */
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;
/* miro/pinnacle + Aimslab VHX
philips matchbox (tea5757 radio tuner) support */
/* locking */
spinlock_t s_lock;
struct semaphore lock;
int resources;
struct semaphore reslock;
/* video state */
int input;
int audio;
unsigned long freq;
int tvnorm,hue,contrast,bright,saturation;
struct video_buffer fbuf;
int field_count;
/* various options */
int opt_combfilter;
int opt_lumafilter;
int opt_automute;
int opt_chroma_agc;
int opt_adc_crush;
/* vbi data/state */
struct bttv_vbi vbi;
/* radio data/state */
int has_radio;
int has_matchbox;
int mbox_we;
int mbox_we;
int mbox_data;
int mbox_clk;
int mbox_most;
int mbox_mask;
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;
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;
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 radio_user;
/* risc memory management data
- must aquire s_lock before changing these
- only the irq handler is supported to touch odd + even */
struct bttv_riscmem main;
struct bttv_buffer *odd; /* current active odd field */
struct bttv_buffer *even; /* current active even field */
struct bttv_buffer *screen; /* overlay */
struct list_head capture; /* capture buffer queue */
struct bttv_buffer *vcurr;
struct list_head vcapture;
unsigned long cap_ctl;
unsigned long dma_on;
struct timer_list timeout;
int errors;
int needs_restart;
wait_queue_head_t gpioq;
int shutdown;
int user;
struct bttv_fh init;
};
/* private ioctls */
#define BTTV_VERSION _IOR('v' , BASE_VIDIOCPRIVATE+6, int)
#define BTTV_VBISIZE _IOR('v' , BASE_VIDIOCPRIVATE+8, int)
#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_ */
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment