Commit effdd2b7 authored by Gerd Knorr's avatar Gerd Knorr Committed by Linus Torvalds

[PATCH] bttv driver update

This patch updates the bttv driver.  Changes:

 * moved much code to the generic video-buf.c helper module
   (bttv-driver.c, bttv-vbi.c, videobuf.c).
 * a number of changes in the card list and the card-specific code
   (bttv-cards.c).
 * misc small fixes here and there.
parent 76f0c877
......@@ -63,9 +63,9 @@ static void rv605_muxsel(struct bttv *btv, unsigned int input);
static int triton1=0;
static int vsfx=0;
int no_overlay=-1;
static unsigned int card[4] = { -1, -1, -1, -1 };
static unsigned int pll[4] = { -1, -1, -1, -1 };
static unsigned int tuner[4] = { -1, -1, -1, -1 };
static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = -1};
static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = -1};
static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = -1};
#ifdef MODULE
static unsigned int autoload = 1;
#else
......@@ -83,11 +83,11 @@ MODULE_PARM(vsfx,"i");
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(card,"1-" __MODULE_STRING(BTTV_MAX) "i");
MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
MODULE_PARM(pll,"1-4i");
MODULE_PARM(pll,"1-" __MODULE_STRING(BTTV_MAX) "i");
MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
MODULE_PARM(tuner,"1-4i");
MODULE_PARM(tuner,"1-" __MODULE_STRING(BTTV_MAX) "i");
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)");
......@@ -135,9 +135,10 @@ 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" },
{ 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" },
{ 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCV3/PCI" },
{ 0x405010fc, BTTV_GVBCTV4PCI, "I-O Data Co. GV-BCV4/PCI" },
{ 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" },
{ 0x405010fc, BTTV_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" },
{ 0x1200bd11, BTTV_PINNACLE, "Pinnacle PCTV" },
{ 0x001211bd, BTTV_PINNACLE, "Pinnacle PCTV" },
......@@ -163,6 +164,8 @@ static struct CARD {
{ 0x111a153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x1123153b, BTTV_TERRATVRADIO, "Terratec TV Radio+" },
{ 0x1127153b, BTTV_TERRATV, "Terratec TV+" },
// clashes with FlyVideo
//{ 0x18521852, BTTV_TERRATV, "Terratec TV+" },
{ 0x1134153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" },
{ 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" },
......@@ -174,13 +177,14 @@ static struct CARD {
{ 0x010115cb, BTTV_GMV1, "AG GMV1" },
{ 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" },
{ 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" },
{ 0xa0fca1a0, BTTV_ZOLTRIX, "Face to Face Tvmax" },
{ 0, -1, NULL }
};
......@@ -235,16 +239,16 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x04 ---------------------------------- */
name: "Intel",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
svhs: -1,
gpiomask: 7,
name: "Intel Create and Share PCI/ Smart Video Recorder III",
video_inputs: 4,
audio_inputs: 0,
tuner: -1,
svhs: 2,
gpiomask: 0,
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 1, 2, 3, 4},
needs_tvaudio: 1,
tuner_type: -1,
audiomux: { 0 },
needs_tvaudio: 0,
tuner_type: 4,
},{
name: "Diamond DTV2000",
video_inputs: 4,
......@@ -283,7 +287,7 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x08 ---------------------------------- */
name: "FlyVideo II (Bt848) LR26",
name: "Lifeview FlyVideo II (Bt848) LR26",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -295,7 +299,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: -1,
},{
name: "IXMicro TurboTV",
name: "IMS/IXmicro TurboTV",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -303,8 +307,9 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 3,
muxsel: { 2, 3, 1, 1},
audiomux: { 1, 1, 2, 3, 0},
needs_tvaudio: 1,
tuner_type: -1,
needs_tvaudio: 0,
pll: PLL_28,
tuner_type: TUNER_TEMIC_PAL,
},{
name: "Hauppauge (bt878)",
video_inputs: 4,
......@@ -331,7 +336,7 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x0c ---------------------------------- */
name: "ADS Technologies Channel Surfer TV",
name: "ADS Technologies Channel Surfer TV (bt848)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -363,6 +368,7 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
},{
name: "Zoltrix TV-Max",
......@@ -378,7 +384,7 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x10 ---------------------------------- */
name: "Pixelview PlayTV (bt878)",
name: "Prolink Pixelview PlayTV (bt878)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -414,8 +420,8 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1,
tuner_type: -1,
},{
name: "LifeView FlyKit w/o Tuner",
video_inputs: 3,
name: "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)",
video_inputs: 4,
audio_inputs: 1,
tuner: -1,
svhs: -1,
......@@ -435,19 +441,18 @@ struct tvcard bttv_tvcards[] = {
muxsel: {2, 3, 1, 1},
tuner_type: -1,
},{
name: "Lucky Star Image World ConferenceTV",
video_inputs: 3,
audio_inputs: 1,
name: "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50",
video_inputs: 4,
audio_inputs: 2, // tuner, line in
tuner: 0,
svhs: 2,
gpiomask: 0x00fffe07,
gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1},
audiomux: { 131072, 1, 1638400, 3, 4},
needs_tvaudio: 1,
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800},
pll: PLL_28,
tuner_type: TUNER_PHILIPS_PAL_I,
},{
name: "Phoebe Tv Master + FM (CPH050)",
name: "Askey CPH050/ Phoebe Tv Master + FM",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -456,6 +461,7 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1},
audiomux: {0, 1, 0x800, 0x400, 0xc00, 0},
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
},{
name: "Modular Technology MM205 PCTV, bt878",
......@@ -472,7 +478,7 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x18 ---------------------------------- */
name: "[many vendors] CPH05X/06X (bt878)",
name: "Askey CPH05X/06X (bt878) [many vendors]",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -484,7 +490,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: -1,
},{
name: "Terratec/Vobis TV-Boostar",
name: "Terratec Terra TV+ Version 1.0 (Bt848)/Vobis TV-Boostar",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -495,7 +501,7 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1,
tuner_type: -1,
},{
name: "Newer Hauppauge WinCam (bt878)",
name: "Hauppauge WinCam newer (bt878)",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -506,15 +512,15 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1,
tuner_type: -1,
},{
name: "MAXI TV Video PCI2",
video_inputs: 3,
audio_inputs: 1,
name: "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50",
video_inputs: 4,
audio_inputs: 2,
tuner: 0,
svhs: 2,
gpiomask: 0xffff,
gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 1, 2, 3, 0xc00},
needs_tvaudio: 1,
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800},
pll: PLL_28,
tuner_type: TUNER_PHILIPS_SECAM,
},{
......@@ -543,18 +549,18 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1,
tuner_type: -1,
},{
name: "FlyVideo 98",
video_inputs: 3,
name: "Lifeview FlyVideo 98 LR50",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
svhs: 2,
gpiomask: 0x1800, //0x8dfe00
muxsel: {2, 3, 1, 1},
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x0800, 0x1000, 0x1000, 0x1800, 0 },
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
},{
name: "iProTV",
name: "Formac iProTV",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -566,16 +572,16 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x20 ---------------------------------- */
name: "Intel Create and Share PCI",
name: "Intel Create and Share PCI/ Smart Video Recorder III",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
audio_inputs: 0,
tuner: -1,
svhs: 2,
gpiomask: 7,
gpiomask: 0,
muxsel: { 2, 3, 1, 1},
audiomux: { 4, 4, 4, 4, 4},
needs_tvaudio: 1,
tuner_type: -1,
audiomux: { 0 },
needs_tvaudio: 0,
tuner_type: 4,
},{
name: "Terratec TerraTValue",
video_inputs: 3,
......@@ -602,34 +608,32 @@ struct tvcard bttv_tvcards[] = {
tuner_type: -1,
audio_hook: winfast2000_audio,
},{
name: "Flyvideo 98 (LR50Q) / Chronos Video Shuttle II",
video_inputs: 3,
name: "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
video_inputs: 4,
audio_inputs: 3,
tuner: 0,
svhs: 2,
gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800},
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
},{
/* ---- card 0x24 ---------------------------------- */
name: "Flyvideo 98FM (LR50Q) / Typhoon TView TV/FM Tuner",
video_inputs: 3,
name: "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner",
video_inputs: 4,
audio_inputs: 3,
tuner: 0,
svhs: 2,
gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 },
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
has_radio: 1,
},{
name: "PixelView PlayTV pro",
name: "Prolink PixelView PlayTV pro",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -641,7 +645,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: -1,
},{
name: "TView99 CPH06X",
name: "Askey CPH06X TView99",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -684,7 +688,7 @@ struct tvcard bttv_tvcards[] = {
audio_inputs: 4,
tuner: 0,
svhs: 2,
gpiomask: 12,
gpiomask: 15,
muxsel: { 2, 3, 1, 1},
audiomux: { 13, 4, 11, 7, 0, 0},
needs_tvaudio: 1,
......@@ -700,6 +704,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0,
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0, 0, 0, 0},
needs_tvaudio: 1,
no_msp34xx: 1,
pll: PLL_28,
tuner_type: 1,
......@@ -758,9 +763,9 @@ struct tvcard bttv_tvcards[] = {
audio_inputs: 1,
tuner: 0,
svhs: 2,
gpiomask: 0x1f0000,
gpiomask: 0x70000,
muxsel: { 2, 3, 1, 1},
audiomux: { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff },
audiomux: { 0x20000, 0x30000, 0x10000, 0, 0x40000, 0x20000 },
needs_tvaudio: 1,
no_msp34xx: 1,
pll: PLL_35,
......@@ -769,7 +774,7 @@ struct tvcard bttv_tvcards[] = {
},{
/* ---- card 0x30 ---------------------------------- */
name: "Dynalink Magic TView ",
name: "Askey CPH03x/ Dynalink Magic TView",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -781,7 +786,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: -1,
},{
name: "GV-BCTV3",
name: "IODATA GV-BCTV3/PCI",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -828,6 +833,15 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x03000F,
muxsel: { 2, 3, 1, 1},
audiomux: { 1, 0x10001, 0, 0, 10},
/* sound path (5 sources):
MUX1 (mask 0x03), Enable Pin 0x08 (0=enable, 1=disable)
0= ext. Audio IN
1= from MUX2
2= Mono TV sound from Tuner
3= not connected
MUX2 (mask 0x30000):
0,2,3= from MSP34xx
1= FM stereo Radio from Tuner */
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1,
......@@ -852,7 +866,7 @@ struct tvcard bttv_tvcards[] = {
options bttv card=0 pll=1 radio=1 gpiomask=0x18e0
audiomux=0x44c71f,0x44d71f,0,0x44d71f,0x44dfff
options tuner type=5 */
name: "Lifetec LT 9415 TV (LR90 Rev.F)",
name: "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -860,17 +874,16 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x18e0,
muxsel: { 2, 3, 1, 1},
audiomux: { 0x0000,0x0800,0x1000,0x1000,0x18e0 },
/* 0x0000: Tuner normal stereo
/* For cards with tda9820/tda9821:
0x0000: Tuner normal stereo
0x0080: Tuner A2 SAP (second audio program = Zweikanalton)
0x0880: Tuner A2 stereo */
pll: PLL_28,
tuner_type: TUNER_PHILIPS_PAL,
audio_hook: lt9415_audio,
has_radio: 1,
tuner_type: -1,
},{
/* Miguel Angel Alvarez <maacruz@navegalia.com>
old Easy TV BT848 version (model CPH031) */
name: "BESTBUY Easy TV (CPH031)",
name: "Askey CPH031/ BESTBUY Easy TV",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -885,15 +898,14 @@ struct tvcard bttv_tvcards[] = {
/* ---- card 0x38 ---------------------------------- */
/* Gordon Heydon <gjheydon@bigfoot.com ('98) */
name: "FlyVideo '98/FM",
video_inputs: 3,
name: "Lifeview FlyVideo 98FM LR50",
video_inputs: 4,
audio_inputs: 3,
tuner: 0,
svhs: 2,
gpiomask: 0x1800,
muxsel: { 2, 3, 0, 1},
muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 },
needs_tvaudio: 1,
pll: PLL_28,
tuner_type: 5,
},{
......@@ -914,7 +926,7 @@ struct tvcard bttv_tvcards[] = {
tuner_type: -1,
},{
/* Daniel Herrington <daniel.herrington@home.com> */
name: "Phoebe TV Master Only (No FM) CPH060",
name: "Askey CPH060/ Phoebe TV Master Only (No FM)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -923,11 +935,11 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1},
audiomux: { 0x400, 0x400, 0x400, 0x400, 0x800, 0x400 },
needs_tvaudio: 1,
pll: PLL_NONE,
pll: PLL_28,
tuner_type: TUNER_TEMIC_4036FY5_NTSC,
},{
/* Matti Mottus <mottus@physic.ut.ee> */
name: "TV Capturer (CPH03X)",
name: "Askey CPH03x TV Capturer",
video_inputs: 4,
audio_inputs: 1,
tuner: 0,
......@@ -944,10 +956,12 @@ struct tvcard bttv_tvcards[] = {
name: "Modular Technology MM100PCTV",
video_inputs: 2,
audio_inputs: 2,
tuner: 0,
svhs: -1,
gpiomask: 11,
muxsel: { 2, 3, 1, 1},
audiomux: { 2, 0, 0, 1, 8},
pll: PLL_NONE,
pll: PLL_35,
tuner_type: TUNER_TEMIC_PAL,
},{
......@@ -968,7 +982,7 @@ struct tvcard bttv_tvcards[] = {
/* Miguel Angel Alvarez <maacruz@navegalia.com>
new Easy TV BT878 version (model CPH061)
special thanks to Informatica Mieres for providing the card */
name: "BESTBUY Easy TV (bt878)",
name: "Askey CPH061/ BESTBUY Easy TV (bt878)",
video_inputs: 3,
audio_inputs: 2,
tuner: 0,
......@@ -1008,7 +1022,7 @@ struct tvcard bttv_tvcards[] = {
tuner_type: TUNER_TEMIC_4006FN5_MULTI_PAL,
},{
/* DeeJay <deejay@westel900.net (2000S) */
name: "FlyVideo 2000S",
name: "Lifeview FlyVideo 2000S LR90",
video_inputs: 3,
audio_inputs: 3,
tuner: 0,
......@@ -1016,7 +1030,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x18e0,
muxsel: { 2, 3, 0, 1},
/* Radio changed from 1e80 to 0x800 to make
Flyvideo2000S in .hu happy (gm)*/
FlyVideo2000S in .hu happy (gm)*/
/* -dk-???: set mute=0x1800 for tda9874h daughterboard */
audiomux: { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 },
audio_hook: fv2000s_audio,
......@@ -1040,7 +1054,7 @@ struct tvcard bttv_tvcards[] = {
has_radio: 1,
},{
/* TANAKA Kei <peg00625@nifty.com> */
name: "GV-BCTV4/PCI",
name: "IODATA GV-BCTV4/PCI",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -1084,7 +1098,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0
},{
/* Tomasz Pyra <hellfire@sedez.iq.pl> */
name: "PV-BT878P+",
name: "Prolink Pixelview PV-BT878P+ (Rev.4C)",
video_inputs: 3,
audio_inputs: 4,
tuner: 0,
......@@ -1096,7 +1110,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: 25,
},{
name: "Flyvideo 98EZ (capture only)",
name: "Lifeview FlyVideo 98EZ (capture only) LR51",
video_inputs: 4,
audio_inputs: 0,
tuner: -1,
......@@ -1108,7 +1122,7 @@ struct tvcard bttv_tvcards[] = {
/* ---- card 0x48 ---------------------------------- */
/* Dariusz Kowalewski <darekk@automex.pl> */
name: "Prolink PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)",
name: "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)",
video_inputs: 3,
audio_inputs: 1,
tuner: 0,
......@@ -1141,8 +1155,8 @@ struct tvcard bttv_tvcards[] = {
name: "RemoteVision MX (RV605)",
video_inputs: 16,
audio_inputs: 0,
tuner: 0,
svhs: 0,
tuner: -1,
svhs: -1,
gpiomask: 0x00,
gpiomask2: 0x07ff,
muxsel: { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03,
......@@ -1155,6 +1169,7 @@ struct tvcard bttv_tvcards[] = {
name: "Powercolor MTV878/ MTV878R/ MTV878F",
video_inputs: 3,
audio_inputs: 2,
tuner: 0,
svhs: 2,
gpiomask: 0x1C800F, // Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset
muxsel: { 2, 1, 1, },
......@@ -1191,15 +1206,42 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28,
tuner_type: -1,
},{
/* http://www.aopen.com/products/video/va1000.htm */
name: "AOPEN VA1000",
video_inputs: 3, /* coax, AV, s-vid */
audio_inputs: 1,
tuner: 0,
tuner_type: TUNER_LG_PAL, /* actually TP18PSB12P (PAL B/G) */
audiomux: { 2, 0, 0, 0 },
muxsel: { 2, 3, 1, 0 },
pll: PLL_28,
name: "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF",
video_inputs: 4,
audio_inputs: 3,
tuner: 0,
svhs: 2,
gpiomask: 7,
muxsel: { 2, 3, 1, 1 }, // Tuner, SVid, SVHS, SVid to SVHS connector
audiomux: { 0 ,0 ,4, 4,4,4},// Yes, this tuner uses the same audio output for TV and FM radio!
// This card lacks external Audio In, so we mute it on Ext. & Int.
// The PCB can take a sbx1637/sbx1673, wiring unknown.
// This card lacks PCI subsystem ID, sigh.
// audiomux=1: lower volume, 2+3: mute
// btwincap uses 0x80000/0x80003
needs_tvaudio: 0,
no_msp34xx: 1,
pll: PLL_28,
tuner_type: 5, // Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and
// radio signal strength indicators work fine.
has_radio: 1,
/* GPIO Info:
GPIO0,1: HEF4052 A0,A1
GPIO2: HEF4052 nENABLE
GPIO3-7: n.c.
GPIO8-13: IRDC357 data0,5 (data6 n.c. ?) [chip not present on my card]
GPIO14,15: ??
GPIO16-21: n.c.
GPIO22,23: ??
?? : mtu8b56ep microcontroller for IR (GPIO wiring unknown)*/
},{
/* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */
name: "DSP Design TCVIDEO",
video_inputs: 4,
svhs: -1,
muxsel: { 2, 3, 1, 0},
pll: PLL_28,
tuner_type: -1,
}};
const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard));
......@@ -1289,8 +1331,8 @@ void __devinit bttv_idcard(struct bttv *btv)
*/
static void flyvideo_gpio(struct bttv *btv)
{
int gpio,outbits;
{
int gpio,outbits,has_remote,has_radio,is_capture_only,is_lr90,has_tda9820_tda9821;
int tuner=-1,ttype;
outbits = btread(BT848_GPIO_OUT_EN);
......@@ -1299,29 +1341,54 @@ static void flyvideo_gpio(struct bttv *btv)
gpio=btread(BT848_GPIO_DATA);
btwrite(outbits, BT848_GPIO_OUT_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.
// GPIO14-12: n.c.
// LR90: GP9=GPIO23 .. GP1=GPIO15 (right above the bt878)
// lowest 3 bytes are remote control codes (no handshake needed)
// xxxFFF: No remote control chip soldered
// xxxF00(LR26/LR50), xxxFE0(LR90): Remote control chip (LVA001 or CF45) soldered
// Note: Some bits are Audio_Mask !
ttype=(gpio&0x0f0000)>>16;
switch(ttype) {
case 0: tuner=4; // None
case 0x0: tuner=4; // None
break;
case 4: tuner=5; // Philips PAL
case 0x4: tuner=5; // Philips PAL
break;
case 6: tuner=37; // LG PAL (newer TAPC series)
case 0x6: 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_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;
has_remote = gpio & 0x800000;
has_radio = gpio & 0x400000;
// unknown 0x200000;
// unknown2 0x100000;
is_capture_only = !(gpio & 0x008000); //GPIO15
has_tda9820_tda9821 = !(gpio & 0x004000);
is_lr90 = !(gpio & 0x002000); // else LR26/LR50 (LR38/LR51 f. capture only)
// gpio & 0x001000 // output bit for audio routing
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);
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 ",
is_capture_only?"yes":"no ");
if(tuner!= -1) // only set if known tuner autodetected, else let insmod option through
btv->tuner_type = tuner;
btv->has_radio = has_radio;
// LR90 Audio Routing is done by 2 hef4052, so Audio_Mask has 4 bits: 0x001c80
// LR26/LR50 only has 1 hef4052, Audio_Mask 0x000c00
// Audio options: from tuner, from tda9821/tda9821(mono,stereo.sap), from tda9874, ext., mute
if(has_tda9820_tda9821) btv->audio_hook = lt9415_audio;
//todo: if(has_tda9874) btv->audio_hook = fv2000s_audio;
}
int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
......@@ -1329,69 +1396,70 @@ int miro_tunermap[] = { 0,6,2,3, 4,5,6,0, 3,0,4,5, 5,2,16,1,
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 };
static void miro_pinnacle_gpio(struct bttv *btv)
{
int id,msp;
id = ((btread(BT848_GPIO_DATA)>>10) & 31) -1;
msp = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx");
btv->tuner_type = miro_tunermap[id];
if (0 == (btread(BT848_GPIO_DATA) & 0x20)) {
btv->has_radio = 1;
if (!miro_fmtuner[id]) {
btv->has_matchbox = 1;
btv->mbox_we = (1<<6);
btv->mbox_most = (1<<7);
btv->mbox_clk = (1<<8);
btv->mbox_data = (1<<9);
btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9);
}
} else {
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;
}
printk(KERN_INFO "bttv%d: miro: id=%d tuner=%d radio=%s stereo=%s\n",
btv->nr, id+1, btv->tuner_type,
!btv->has_radio ? "no" :
(btv->has_matchbox ? "matchbox" : "fmtuner"),
(-1 == msp) ? "no" : "yes");
}
/* 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);
if (btv->type == BTTV_VOODOOTV_FM)
boot_msp34xx(btv,20);
}
/* initialization part one -- after registering i2c bus */
/* initialization part two -- after registering i2c bus */
void __devinit bttv_init_card2(struct bttv *btv)
{
/* miro/pinnacle */
btv->tuner_type = -1;
/* miro/pinnacle */
if (btv->type == BTTV_MIRO ||
btv->type == BTTV_MIROPRO ||
btv->type == BTTV_PINNACLE ||
btv->type == BTTV_PINNACLEPRO) {
int id,msp;
id = ((btread(BT848_GPIO_DATA)>>10) & 31) -1;
msp = bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx");
btv->tuner_type = miro_tunermap[id];
if (0 == (btread(BT848_GPIO_DATA) & 0x20)) {
btv->has_radio = 1;
if (!miro_fmtuner[id]) {
btv->has_matchbox = 1;
btv->mbox_we = (1<<6);
btv->mbox_most = (1<<7);
btv->mbox_clk = (1<<8);
btv->mbox_data = (1<<9);
btv->mbox_mask = (1<<6)|(1<<7)|(1<<8)|(1<<9);
}
} else {
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 (bttv_verbose)
printk(KERN_INFO "bttv%d: miro: id=%d tuner=%d "
"radio=%s stereo=%s\n",
btv->nr, id+1, btv->tuner_type,
!btv->has_radio ? "no" :
(btv->has_matchbox ? "matchbox" : "fmtuner"),
(-1 == msp) ? "no" : "yes");
#if 0
if (btv->has_matchbox) {
if (bttv_verbose)
printk(KERN_INFO "Initializing TEA5757...\n");
init_tea5757(btv);
}
#endif
}
btv->type == BTTV_MIROPRO ||
btv->type == BTTV_PINNACLE ||
btv->type == BTTV_PINNACLEPRO)
miro_pinnacle_gpio(btv);
if (btv->type == BTTV_FLYVIDEO_98 ||
btv->type == BTTV_LIFE_FLYKIT ||
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)
btv->type == BTTV_FLYVIDEO98EZ ||
btv->type == BTTV_CONFERENCETV ||
btv->type == BTTV_LIFETEC_9415)
flyvideo_gpio(btv);
if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) {
......@@ -1418,13 +1486,6 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->mbox_mask = 0x38;
}
if (btv->type == BTTV_LIFETEC_9415) {
if (btread(BT848_GPIO_DATA) & 0x4000)
printk("bttv%d: lifetec: tv mono/fm stereo card\n", btv->nr);
else
printk("bttv%d: lifetec: stereo(TDA9821) card\n",btv->nr);
}
if (btv->type == BTTV_MAGICTVIEW061) {
if(btv->cardid == 0x4002144f) {
btv->has_radio=1;
......@@ -1465,17 +1526,22 @@ void __devinit bttv_init_card2(struct bttv *btv)
}
}
/* tuner configuration (from card list / insmod option) */
/* tuner configuration (from card list / autodetect / insmod option) */
if (-1 != bttv_tvcards[btv->type].tuner_type)
btv->tuner_type = bttv_tvcards[btv->type].tuner_type;
if( -1 == btv->tuner_type)
btv->tuner_type = bttv_tvcards[btv->type].tuner_type;
if (-1 != tuner[btv->nr])
btv->tuner_type = tuner[btv->nr];
if (btv->tuner_type != -1)
bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type);
printk("bttv%d: using tuner=%d\n",btv->nr,btv->tuner_type);
if (bttv_tvcards[btv->type].has_radio)
btv->has_radio=1;
if (bttv_tvcards[btv->type].audio_hook)
btv->audio_hook=bttv_tvcards[btv->type].audio_hook;
/* try to detect audio/fader chips */
if (!bttv_tvcards[btv->type].no_msp34xx &&
bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) {
......
......@@ -80,7 +80,7 @@ static unsigned int v4l2 = 1;
/* insmod args */
MODULE_PARM(radio,"1-4i");
MODULE_PARM(radio,"1-" __MODULE_STRING(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");
......@@ -762,7 +762,7 @@ video_mux(struct bttv *btv, unsigned int input)
btand(~(3<<5), BT848_IFORM);
schedule_timeout(HZ/10);
#endif
if (input==bttv_tvcards[btv->type].svhs) {
btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
......@@ -789,10 +789,11 @@ static char *audio_modes[] = {
static int
audio_mux(struct bttv *btv, int mode)
{
int val,mux;
int val,mux,i2c_mux,signal;
btaor(bttv_tvcards[btv->type].gpiomask,
~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN);
signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
switch (mode) {
case AUDIO_MUTE:
......@@ -808,16 +809,19 @@ audio_mux(struct bttv *btv, int mode)
btv->audio &= AUDIO_MUTE;
btv->audio |= mode;
}
dprintk("bttv%d: audio mux: mode=%d audio=%d\n",
btv->nr,mode,btv->audio);
i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
if (btv->opt_automute && !signal && !btv->radio_user)
mux = AUDIO_OFF;
printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n",
btv->nr, mode, btv->audio, signal ? "yes" : "no",
mux, i2c_mux, in_interrupt() ? "yes" : "no");
mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
val = bttv_tvcards[btv->type].audiomux[mux];
btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
if (bttv_gpio)
bttv_gpio_tracking(btv,audio_modes[mux]);
if (!in_interrupt())
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mux));
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux));
return 0;
}
......@@ -830,6 +834,8 @@ i2c_vidiocschan(struct bttv *btv)
c.norm = btv->tvnorm;
c.channel = btv->input;
bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
if (btv->type == BTTV_VOODOOTV_FM)
bttv_tda9880_setnorm(btv,c.norm);
}
static int
......@@ -931,8 +937,8 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
memset(&va,0,sizeof(va));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0);
if (btv->audio_hook)
btv->audio_hook(btv,&va,0);
}
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
......@@ -998,8 +1004,8 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
memset(&va,0,sizeof(va));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0);
if (btv->audio_hook)
btv->audio_hook(btv,&va,0);
}
switch (c->id) {
case V4L2_CID_BRIGHTNESS:
......@@ -1069,8 +1075,8 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
}
if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,1);
if (btv->audio_hook)
btv->audio_hook(btv,&va,1);
}
return 0;
}
......@@ -1161,35 +1167,6 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
return retval;
}
static void
bttv_stop_streaming(struct bttv *btv, struct bttv_fh *fh)
{
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 == fh->bufs[i])
continue;
if (fh->bufs[i]->vb.state == STATE_QUEUED) {
list_del(&fh->bufs[i]->vb.queue);
fh->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 == fh->bufs[i])
continue;
bttv_dma_free(fh->btv,fh->bufs[i]);
}
INIT_LIST_HEAD(&fh->stream);
free_btres(btv,fh,RESOURCE_STREAMING);
bttv_field_count(btv);
}
/* ----------------------------------------------------------------------- */
/* video4linux (1) interface */
......@@ -1218,26 +1195,28 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
buf->vb.size = (width * height * fmt->depth) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL;
field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN,
btv->tvnorm, height);
}
/* 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;
}
if (STATE_NEEDS_INIT == buf->vb.state) {
field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN,
btv->tvnorm, height);
if (field != buf->vb.field)
redo_dma_risc = 1;
#if 0
if (STATE_NEEDS_INIT == buf->vb.state)
if (redo_dma_risc)
bttv_dma_free(btv,buf);
}
#endif
/* alloc risc memory */
if (STATE_NEEDS_INIT == buf->vb.state) {
......@@ -1258,18 +1237,55 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
return rc;
}
static void
bttv_queue_buffer(struct bttv *btv, struct bttv_buffer *buf)
static int
buffer_setup(struct file *file, int *count, int *size)
{
unsigned long flags;
struct bttv_fh *fh = file->private_data;
*size = fh->buf.fmt->depth*fh->buf.vb.width*fh->buf.vb.height >> 3;
if (0 == *count)
*count = gbuffers;
while (*size * *count > gbuffers * gbufsize)
(*count)--;
return 0;
}
static int
buffer_prepare(struct file *file, struct videobuf_buffer *vb, int field)
{
struct bttv_buffer *buf = (struct bttv_buffer*)vb;
struct bttv_fh *fh = file->private_data;
return bttv_prepare_buffer(fh->btv,buf,fh->buf.fmt,
fh->buf.vb.width,fh->buf.vb.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;
spin_lock_irqsave(&btv->s_lock,flags);
list_add_tail(&buf->vb.queue,&btv->capture);
bttv_set_dma(btv, 0x03, 1);
spin_unlock_irqrestore(&btv->s_lock,flags);
list_add_tail(&buf->vb.queue,&fh->btv->capture);
bttv_set_dma(fh->btv, 0x03, 1);
}
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_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",
......@@ -1413,8 +1429,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
bttv_call_i2c_clients(btv,cmd,v);
/* card specific hooks */
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,v,0);
if (btv->audio_hook)
btv->audio_hook(btv,v,0);
up(&btv->lock);
return 0;
......@@ -1432,8 +1448,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
bttv_call_i2c_clients(btv,cmd,v);
/* card specific hooks */
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,v,1);
if (btv->audio_hook)
btv->audio_hook(btv,v,1);
up(&btv->lock);
return 0;
......@@ -1533,8 +1549,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
struct video_audio va;
memset(&va, 0, sizeof(struct video_audio));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0);
if (btv->audio_hook)
btv->audio_hook(btv,&va,0);
if(va.mode & VIDEO_SOUND_STEREO)
t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
if(va.mode & VIDEO_SOUND_LANG1)
......@@ -1564,8 +1580,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
else if (t->audmode == V4L2_TUNER_MODE_LANG2)
va.mode = VIDEO_SOUND_LANG2;
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,1);
if (btv->audio_hook)
btv->audio_hook(btv,&va,1);
}
up(&btv->lock);
return 0;
......@@ -1616,7 +1632,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
clips, n);
bttv_sort_clips(clips,nclips);
down(&fh->lock);
down(&fh->q.lock);
if (fh->ov.clips)
kfree(fh->ov.clips);
fh->ov.clips = clips;
......@@ -1634,27 +1650,20 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
if (check_btres(fh, RESOURCE_OVERLAY)) {
struct bttv_buffer *new;
new = videobuf_alloc(sizeof(*new),V4L2_BUF_TYPE_CAPTURE);
new = videobuf_alloc(sizeof(*new));
bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
retval = bttv_switch_overlay(btv,fh,new);
}
up(&fh->lock);
up(&fh->q.lock);
return retval;
}
static void release_buffer(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 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;
if (bttv_debug > 1) {
......@@ -1723,7 +1732,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
fmt = format_by_palette(pic->palette);
if (NULL == fmt)
return -EINVAL;
down(&fh->lock);
down(&fh->q.lock);
retval = -EINVAL;
if (fmt->depth != pic->depth && !sloppy)
goto fh_unlock_and_return;
......@@ -1744,7 +1753,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
bt848_contrast(btv,pic->contrast);
bt848_hue(btv,pic->hue);
bt848_sat(btv,pic->colour);
up(&fh->lock);
up(&fh->q.lock);
return 0;
}
......@@ -1796,7 +1805,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
fbuf->height * fbuf->bytesperline;
if (0 == find_videomem((unsigned long)fbuf->base,end))
return -EINVAL;
down(&fh->lock);
down(&fh->q.lock);
retval = -EINVAL;
if (sloppy) {
/* also set the default palette -- for backward
......@@ -1836,7 +1845,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
goto fh_unlock_and_return;
}
btv->fbuf = *fbuf;
up(&fh->lock);
up(&fh->q.lock);
return 0;
}
......@@ -1863,11 +1872,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
return -EBUSY;
down(&fh->lock);
down(&fh->q.lock);
if (*on) {
fh->ov.tvnorm = btv->tvnorm;
new = videobuf_alloc(sizeof(*new),
V4L2_BUF_TYPE_CAPTURE);
new = videobuf_alloc(sizeof(*new));
bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
} else {
new = NULL;
......@@ -1875,7 +1883,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
/* switch over */
retval = bttv_switch_overlay(btv,fh,new);
up(&fh->lock);
up(&fh->q.lock);
return retval;
}
......@@ -1886,12 +1894,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (!mmap)
return -EINVAL;
down(&fh->lock);
retval = videobuf_mmap_setup
(file,(struct videobuf_buffer**)fh->bufs,
sizeof(struct bttv_buffer),
gbuffers,gbufsize,V4L2_BUF_TYPE_CAPTURE,
release_buffer);
down(&fh->q.lock);
retval = videobuf_mmap_setup(file,&fh->q,gbuffers,gbufsize);
if (retval < 0)
goto fh_unlock_and_return;
memset(mbuf,0,sizeof(*mbuf));
......@@ -1899,7 +1903,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
mbuf->size = gbuffers * gbufsize;
for (i = 0; i < gbuffers; i++)
mbuf->offsets[i] = i * gbufsize;
up(&fh->lock);
up(&fh->q.lock);
return 0;
}
case VIDIOCMCAPTURE:
......@@ -1910,9 +1914,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (vm->frame >= VIDEO_MAX_FRAME)
return -EINVAL;
down(&fh->lock);
down(&fh->q.lock);
retval = -EINVAL;
buf = fh->bufs[vm->frame];
buf = (struct bttv_buffer *)fh->q.bufs[vm->frame];
if (NULL == buf)
goto fh_unlock_and_return;
if (0 == buf->vb.baddr)
......@@ -1926,8 +1930,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
vm->width,vm->height,0);
if (0 != retval)
goto fh_unlock_and_return;
bttv_queue_buffer(btv,buf);
up(&fh->lock);
spin_lock_irqsave(&btv->s_lock,flags);
buffer_queue(file,&buf->vb);
spin_unlock_irqrestore(&btv->s_lock,flags);
up(&fh->q.lock);
return 0;
}
case VIDIOCSYNC:
......@@ -1938,9 +1944,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (*frame >= VIDEO_MAX_FRAME)
return -EINVAL;
down(&fh->lock);
down(&fh->q.lock);
retval = -EINVAL;
buf = fh->bufs[*frame];
buf = (struct bttv_buffer *)fh->q.bufs[*frame];
if (NULL == buf)
goto fh_unlock_and_return;
retval = videobuf_waiton(&buf->vb,0,1);
......@@ -1958,7 +1964,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
retval = -EINVAL;
break;
}
up(&fh->lock);
up(&fh->q.lock);
return retval;
}
......@@ -2056,7 +2062,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
/* FIXME -- not implemented yet */
return -EINVAL;
down(&fh->lock);
down(&fh->q.lock);
/* fixup format */
if (f->fmt.pix.width > bttv_tvnorms[btv->tvnorm].swidth)
f->fmt.pix.width = bttv_tvnorms[btv->tvnorm].swidth;
......@@ -2087,7 +2093,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
f->fmt.pix.depth = fmt->depth;
f->fmt.pix.sizeimage =
(fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8;
up(&fh->lock);
up(&fh->q.lock);
return 0;
}
......@@ -2130,7 +2136,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
return -EINVAL;
down(&fh->lock);
down(&fh->q.lock);
retval = -EINVAL;
if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth)
......@@ -2167,13 +2173,12 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (check_btres(fh, RESOURCE_OVERLAY)) {
struct bttv_buffer *new;
new = videobuf_alloc(sizeof(*new),
V4L2_BUF_TYPE_CAPTURE);
new = videobuf_alloc(sizeof(*new));
bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
retval = bttv_switch_overlay(btv,fh,new);
}
}
up(&fh->lock);
up(&fh->q.lock);
return retval;
}
case VIDIOC_G_WIN:
......@@ -2198,154 +2203,32 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
}
case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers *req = arg;
int size,count;
if (!mmap)
return -EINVAL;
if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
return -EINVAL;
if (req->count < 0)
return -EINVAL;
return videobuf_reqbufs(file,&fh->q,arg);
down(&fh->lock);
retval = -EINVAL;
if (NULL == fh->buf.fmt ||
0 == fh->buf.vb.width ||
0 == fh->buf.vb.height)
goto fh_unlock_and_return;
size = (fh->buf.vb.width*fh->buf.vb.height*fh->buf.fmt->depth) >> 3;
size = (size + PAGE_SIZE - 1) & PAGE_MASK;
count = req->count;
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
while (size * count > gbuffers * gbufsize)
count--;
retval = videobuf_mmap_setup(file,
(struct videobuf_buffer**)fh->bufs,
sizeof(struct bttv_buffer),
count,size,V4L2_BUF_TYPE_CAPTURE,
release_buffer);
if (retval < 0)
goto fh_unlock_and_return;
req->type = V4L2_BUF_TYPE_CAPTURE;
req->count = count;
up(&fh->lock);
return 0;
}
case VIDIOC_QUERYBUF:
{
struct v4l2_buffer *b = arg;
return videobuf_querybuf(&fh->q,arg);
if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
return -EINVAL;
if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
return -EINVAL;
if (NULL == fh->bufs[b->index])
return -EINVAL;
videobuf_status(b,&fh->bufs[b->index]->vb);
return 0;
}
case VIDIOC_QBUF:
{
struct v4l2_buffer *b = arg;
struct bttv_buffer *buf;
int field = 0;
if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
return -EINVAL;
if (b->index < 0 || b->index > VIDEO_MAX_FRAME)
return -EINVAL;
return videobuf_qbuf(file,&fh->q,arg);
down(&fh->lock);
retval = -EINVAL;
buf = fh->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;
if (b->flags & V4L2_BUF_FLAG_TOPFIELD)
field |= VBUF_FIELD_ODD;
if (b->flags & V4L2_BUF_FLAG_BOTFIELD)
field |= VBUF_FIELD_EVEN;
retval = bttv_prepare_buffer(btv,buf,fh->buf.fmt,
fh->buf.vb.width,fh->buf.vb.height,field);
if (0 != retval)
goto fh_unlock_and_return;
list_add_tail(&buf->vb.stream,&fh->stream);
if (check_btres(fh, RESOURCE_STREAMING))
bttv_queue_buffer(btv,buf);
up(&fh->lock);
return 0;
}
case VIDIOC_DQBUF:
{
struct v4l2_buffer *b = arg;
struct bttv_buffer *buf;
return videobuf_dqbuf(file,&fh->q,arg);
if ((b->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE)
return -EINVAL;
down(&fh->lock);
retval = -EINVAL;
if (list_empty(&fh->stream))
goto fh_unlock_and_return;
buf = list_entry(fh->stream.next, struct bttv_buffer, vb.stream);
retval = videobuf_waiton(&buf->vb,0,1);
if (retval < 0)
goto fh_unlock_and_return;
switch (buf->vb.state) {
case STATE_ERROR:
retval = -EIO;
/* fall through */
case STATE_DONE:
videobuf_dma_pci_sync(btv->dev,&buf->vb.dma);
buf->vb.state = STATE_IDLE;
break;
default:
retval = -EINVAL;
goto fh_unlock_and_return;
}
list_del(&buf->vb.stream);
memset(b,0,sizeof(*b));
videobuf_status(b,&buf->vb);
up(&fh->lock);
return retval;
}
case VIDIOC_STREAMON:
{
struct list_head *list;
struct bttv_buffer *buf;
if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING))
return -EBUSY;
bttv_field_count(btv);
down(&fh->lock);
list_for_each(list,&fh->stream) {
buf = list_entry(list, struct bttv_buffer, vb.stream);
if (buf->vb.state == STATE_PREPARED)
bttv_queue_buffer(btv,buf);
}
up(&fh->lock);
return 0;
}
return videobuf_streamon(file,&fh->q);
case VIDIOC_STREAMOFF:
{
down(&fh->lock);
retval = -EINVAL;
if (!check_btres(fh, RESOURCE_STREAMING))
goto fh_unlock_and_return;
bttv_stop_streaming(btv,fh);
up(&fh->lock);
retval = videobuf_streamoff(file,&fh->q);
if (retval < 0)
return retval;
free_btres(btv,fh,RESOURCE_STREAMING);
bttv_field_count(btv);
return 0;
}
case VIDIOC_QUERYCTRL:
{
......@@ -2370,8 +2253,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
struct video_audio va;
memset(&va,0,sizeof(va));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0);
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))
......@@ -2428,7 +2311,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
return 0;
fh_unlock_and_return:
up(&fh->lock);
up(&fh->q.lock);
return retval;
}
......@@ -2438,23 +2321,7 @@ static int bttv_ioctl(struct inode *inode, struct file *file,
return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
}
/* start capture to a kernel bounce buffer */
static int bttv_read_capture(struct bttv_fh *fh)
{
struct bttv *btv = fh->btv;
int rc;
dprintk("bttv%d: read start\n",btv->nr);
rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,
fh->buf.vb.width,fh->buf.vb.height,0);
if (0 != rc)
return rc;
bttv_queue_buffer(btv,&fh->read_buf);
fh->read_off = 0;
return 0;
}
#if 0
/*
* blocking read for a complete video frame
* => no kernel bounce buffer needed.
......@@ -2467,87 +2334,42 @@ bttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv,
/* setup stuff */
dprintk("bttv%d: read zerocopy\n",btv->nr);
fh->read_buf.vb.baddr = (unsigned long)data;
fh->read_buf.vb.bsize = count;
rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt,
fh->q.read_buf->baddr = (unsigned long)data;
fh->q.read_buf->bsize = count;
rc = bttv_prepare_buffer(btv,fh->q.read_buf,fh->buf.fmt,
fh->buf.vb.width,fh->buf.vb.height,0);
if (0 != rc)
goto done;
/* start capture & wait */
bttv_queue_buffer(btv,&fh->read_buf);
rc = videobuf_waiton(&fh->read_buf.vb,0,1);
bttv_queue_buffer(btv,fh->q.read_buf);
rc = videobuf_waiton(fh->q.read_buf,0,1);
if (0 == rc) {
videobuf_dma_pci_sync(btv->dev,&fh->read_buf.vb.dma);
rc = fh->read_buf.vb.size;
videobuf_dma_pci_sync(btv->dev,&fh->q.read_buf->dma);
rc = fh->q.read_buf->size;
}
done:
/* cleanup */
bttv_dma_free(btv,&fh->read_buf);
fh->read_buf.vb.baddr = 0;
fh->read_buf.vb.size = 0;
bttv_dma_free(btv,fh->q.read_buf);
fh->q.read_buf->baddr = 0;
fh->q.read_buf->size = 0;
return rc;
}
#endif
static ssize_t bttv_read(struct file *file, char *data,
size_t count, loff_t *ppos)
{
struct bttv_fh *fh = file->private_data;
struct bttv *btv = fh->btv;
int rc, bytes, size;
if (btv->errors)
bttv_reinit_bt848(btv);
if (locked_btres(btv,RESOURCE_STREAMING))
if (fh->btv->errors)
bttv_reinit_bt848(fh->btv);
if (locked_btres(fh->btv,RESOURCE_STREAMING))
return -EBUSY;
down(&fh->lock);
size = fh->buf.fmt->depth*fh->buf.vb.width*fh->buf.vb.height >> 3;
if (-1 == fh->read_off && count >= size &&
!(file->f_flags & O_NONBLOCK)) {
rc = bttv_read_zerocopy(fh,btv,data,count,ppos);
if (rc >= 0)
/* ok, all done */
goto unlock_out;
/* fallback to kernel bounce buffer on failures */
}
if (-1 == fh->read_off) {
/* need to capture a new frame */
rc = bttv_read_capture(fh);
if (0 != rc)
goto unlock_out;
}
/* wait until capture is done */
rc = videobuf_waiton(&fh->read_buf.vb, file->f_flags & O_NONBLOCK,1);
if (0 != rc)
goto unlock_out;
/* copy to userspace */
bytes = count;
if (bytes > fh->read_buf.vb.size - fh->read_off)
bytes = fh->read_buf.vb.size - fh->read_off;
rc = -EFAULT;
if (copy_to_user(data,fh->read_buf.vb.dma.vmalloc +
fh->read_off,bytes))
goto unlock_out;
rc = bytes;
fh->read_off += bytes;
dprintk("bttv%d: read %d bytes\n",btv->nr,bytes);
if (fh->read_off == fh->read_buf.vb.size) {
/* all data copied, cleanup */
dprintk("bttv%d: read done\n",btv->nr);
bttv_dma_free(btv,&fh->read_buf);
fh->read_off = -1;
}
unlock_out:
up(&fh->lock);
return rc;
return videobuf_read_one(file, &fh->q, data, count, ppos);
}
static unsigned int bttv_poll(struct file *file, poll_table *wait)
......@@ -2555,24 +2377,34 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
struct bttv_fh *fh = file->private_data;
struct bttv_buffer *buf;
if (check_alloc_btres(fh->btv,fh,RESOURCE_STREAMING)) {
if (check_btres(fh,RESOURCE_STREAMING)) {
/* streaming capture */
if (list_empty(&fh->stream))
if (list_empty(&fh->q.stream))
return POLLERR;
buf = list_entry(fh->stream.next,struct bttv_buffer,vb.stream);
buf = list_entry(fh->q.stream.next,struct bttv_buffer,vb.stream);
} else {
/* read() capture */
down(&fh->lock);
if (-1 == fh->read_off) {
down(&fh->q.lock);
if (NULL == fh->q.read_buf) {
/* need to capture a new frame */
if (locked_btres(fh->btv,RESOURCE_STREAMING) ||
0 != bttv_read_capture(fh)) {
up(&fh->lock);
if (locked_btres(fh->btv,RESOURCE_STREAMING)) {
up(&fh->q.lock);
return POLLERR;
}
fh->q.read_buf = videobuf_alloc(fh->q.msize);
if (NULL == fh->q.read_buf) {
up(&fh->q.lock);
return POLLERR;
}
if (0 != fh->q.ops->buf_prepare(file,fh->q.read_buf,0)) {
up(&fh->q.lock);
return POLLERR;
}
fh->q.ops->buf_queue(file,fh->q.read_buf);
fh->q.read_off = 0;
}
up(&fh->lock);
buf = &fh->read_buf;
up(&fh->q.lock);
buf = (struct bttv_buffer*)fh->q.read_buf;
}
poll_wait(file, &buf->vb.done, wait);
......@@ -2608,11 +2440,9 @@ static int bttv_open(struct inode *inode, struct file *file)
return -ENOMEM;
file->private_data = fh;
*fh = btv->init;
init_MUTEX(&fh->lock);
INIT_LIST_HEAD(&fh->stream);
init_waitqueue_head(&fh->read_buf.vb.done);
fh->read_off = -1;
videobuf_queue_init(&fh->q, &bttv_qops, btv->dev, &btv->s_lock,
V4L2_BUF_TYPE_CAPTURE,sizeof(struct bttv_buffer));
i2c_vidiocschan(btv);
return 0;
}
......@@ -2625,9 +2455,15 @@ static int bttv_release(struct inode *inode, struct file *file)
/* turn off overlay, stop outstanding captures */
if (check_btres(fh, RESOURCE_OVERLAY))
bttv_switch_overlay(btv,fh,NULL);
if (check_btres(fh, RESOURCE_STREAMING))
bttv_stop_streaming(btv,fh);
bttv_dma_free(btv,&fh->read_buf);
if (check_btres(fh, RESOURCE_STREAMING)) {
videobuf_streamoff(file,&fh->q);
free_btres(btv,fh,RESOURCE_STREAMING);
bttv_field_count(btv);
}
if (fh->q.read_buf) {
buffer_release(file,fh->q.read_buf);
kfree(fh->q.read_buf);
}
file->private_data = NULL;
kfree(fh);
......@@ -2638,14 +2474,12 @@ static int
bttv_mmap(struct file *file, struct vm_area_struct *vma)
{
struct bttv_fh *fh = file->private_data;
int err;
if (!mmap)
return -EINVAL;
down(&fh->lock);
err = videobuf_mmap_mapper(vma,(struct videobuf_buffer**)fh->bufs);
up(&fh->lock);
return err;
dprintk("mmap 0x%lx+%ld\n",vma->vm_start,
vma->vm_end - vma->vm_start);
return videobuf_mmap_mapper(vma,&fh->q);
}
static struct file_operations bttv_fops =
......@@ -2786,7 +2620,7 @@ static struct file_operations radio_fops =
static struct video_device radio_template =
{
name: "bt848/878 radio",
type: VID_TYPE_TUNER|VID_TYPE_TELETEXT,
type: VID_TYPE_TUNER,
hardware: VID_HARDWARE_BT848,
fops: &radio_fops,
minor: -1,
......@@ -3075,12 +2909,8 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs)
if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
bttv_irq_switch_fields(btv);
if ((astat & BT848_INT_HLOCK) && btv->opt_automute) {
if ((dstat & BT848_DSTATUS_HLOC) || btv->radio_user)
audio_mux(btv, AUDIO_UNMUTE);
else
audio_mux(btv, AUDIO_MUTE);
}
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->nr,
......@@ -3175,9 +3005,8 @@ static int __devinit bttv_probe(struct pci_dev *dev,
init_waitqueue_head(&btv->gpioq);
INIT_LIST_HEAD(&btv->capture);
INIT_LIST_HEAD(&btv->vcapture);
init_MUTEX(&btv->vbi.lock);
INIT_LIST_HEAD(&btv->vbi.stream);
videobuf_queue_init(&btv->vbi.q, &vbi_qops, btv->dev, &btv->s_lock,
V4L2_BUF_TYPE_VBI,sizeof(struct bttv_buffer));
btv->timeout.function = bttv_irq_timeout;
btv->timeout.data = (unsigned long)btv;
......
......@@ -69,8 +69,21 @@ vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
return 0;
}
static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
static int vbi_buffer_setup(struct file *file, int *count, int *size)
{
struct bttv *btv = file->private_data;
if (0 == *count)
*count = vbibufs;
*size = btv->vbi.lines * 2 * 2048;
return 0;
}
static int vbi_buffer_prepare(struct file *file, struct videobuf_buffer *vb,
int fields)
{
struct bttv *btv = file->private_data;
struct bttv_buffer *buf = (struct bttv_buffer*)vb;
int rc;
buf->vb.size = btv->vbi.lines * 2 * 2048;
......@@ -84,7 +97,8 @@ static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
goto fail;
}
buf->vb.state = STATE_PREPARED;
dprintk("buf prepare ok: odd=%p even=%p\n",&buf->odd,&buf->even);
dprintk("buf prepare %p: odd=%p even=%p\n",
vb,&buf->odd,&buf->even);
return 0;
fail:
......@@ -93,94 +107,35 @@ static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
}
static void
vbi_buffer_queue(struct bttv *btv, struct bttv_buffer *buf)
vbi_buffer_queue(struct file *file, struct videobuf_buffer *vb)
{
unsigned long flags;
struct bttv *btv = file->private_data;
struct bttv_buffer *buf = (struct bttv_buffer*)vb;
dprintk("queue %p\n",vb);
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;
dprintk("free %p\n",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);
}
struct videobuf_queue_ops vbi_qops = {
buf_setup: vbi_buffer_setup,
buf_prepare: vbi_buffer_prepare,
buf_queue: vbi_buffer_queue,
buf_release: vbi_buffer_release,
};
/* ----------------------------------------------------------------------- */
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;
......@@ -234,7 +189,6 @@ static int vbi_open(struct inode *inode, struct file *file)
struct bttv *btv = NULL;
int i;
for (i = 0; i < bttv_num; i++) {
if (bttvs[i].vbi_dev.minor == minor) {
btv = &bttvs[i];
......@@ -244,18 +198,18 @@ static int vbi_open(struct inode *inode, struct file *file)
if (NULL == btv)
return -ENODEV;
down(&btv->vbi.lock);
down(&btv->vbi.q.lock);
if (btv->vbi.users) {
up(&btv->vbi.lock);
up(&btv->vbi.q.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);
bttv_field_count(btv);
up(&btv->vbi.lock);
up(&btv->vbi.q.lock);
return 0;
}
......@@ -264,15 +218,15 @@ 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;
}
if (btv->vbi.q.streaming)
videobuf_streamoff(file,&btv->vbi.q);
down(&btv->vbi.q.lock);
if (btv->vbi.q.reading)
videobuf_read_stop(file,&btv->vbi.q);
btv->vbi.users--;
bttv_field_count(btv);
vbi_setlines(btv,0);
up(&btv->vbi.lock);
up(&btv->vbi.q.lock);
return 0;
}
......@@ -280,10 +234,6 @@ static int vbi_do_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);
......@@ -334,7 +284,7 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
{
struct v4l2_format *f = arg;
if (btv->vbi.reading || btv->vbi.streaming)
if (btv->vbi.q.reading || btv->vbi.q.streaming)
return -EBUSY;
vbi_setlines(btv,f->fmt.vbi.count[0]);
vbi_fmt(btv,f);
......@@ -342,145 +292,22 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
}
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;
}
return videobuf_reqbufs(file,&btv->vbi.q,arg);
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;
}
return videobuf_querybuf(&btv->vbi.q, arg);
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;
}
return videobuf_qbuf(file, &btv->vbi.q, arg);
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;
}
return videobuf_dqbuf(file, &btv->vbi.q, arg);
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;
}
return videobuf_streamon(file, &btv->vbi.q);
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;
}
return videobuf_streamoff(file, &btv->vbi.q);
case VIDIOC_ENUMSTD:
case VIDIOC_G_STD:
......@@ -499,12 +326,6 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
return -ENOIOCTLCMD;
}
return 0;
#ifdef HAVE_V4L2
fh_unlock_and_return:
up(&btv->vbi.lock);
return err;
#endif
}
static int vbi_ioctl(struct inode *inode, struct file *file,
......@@ -516,123 +337,30 @@ static int vbi_ioctl(struct inode *inode, struct file *file,
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;
dprintk("read %d\n",count);
return videobuf_read_stream(file, &btv->vbi.q, data, count, ppos, 1);
}
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;
dprintk("poll%s\n","");
return videobuf_poll_stream(file, &btv->vbi.q, wait);
}
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;
dprintk("mmap 0x%lx+%ld\n",vma->vm_start,
vma->vm_end - vma->vm_start);
return videobuf_mmap_mapper(vma, &btv->vbi.q);
}
static struct file_operations vbi_fops =
......
......@@ -24,7 +24,7 @@
#ifndef _BTTVP_H_
#define _BTTVP_H_
#define BTTV_VERSION_CODE KERNEL_VERSION(0,8,38)
#define BTTV_VERSION_CODE KERNEL_VERSION(0,8,42)
#include <linux/types.h>
#include <linux/wait.h>
......@@ -127,40 +127,20 @@ struct bttv_overlay {
};
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;
int users;
int lines;
struct videobuf_queue q;
};
struct bttv_fh {
struct bttv *btv;
/* locking */
struct videobuf_queue q;
int resources;
struct semaphore lock;
/* keep current driver settings */
/* current 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 */
};
/* ---------------------------------------------------------- */
......@@ -221,6 +201,8 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
/* bttv-vbi.c */
extern struct video_device bttv_vbi_template;
extern struct videobuf_queue_ops vbi_qops;
/* ---------------------------------------------------------- */
/* bttv-driver.c */
......@@ -277,7 +259,8 @@ struct bttv {
/* gpio interface */
wait_queue_head_t gpioq;
int shutdown;
void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
/* i2c layer */
struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo;
......
......@@ -207,14 +207,13 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
/* --------------------------------------------------------------------- */
void* videobuf_alloc(int size, int type)
void* videobuf_alloc(int size)
{
struct videobuf_buffer *vb;
vb = kmalloc(size,GFP_KERNEL);
if (NULL != vb) {
memset(vb,0,size);
vb->type = type;
init_waitqueue_head(&vb->done);
}
return vb;
......@@ -273,12 +272,63 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb)
return 0;
}
/* --------------------------------------------------------------------- */
void
videobuf_queue_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct pci_dev *pci,
spinlock_t *irqlock,
int type,
int msize)
{
memset(q,0,sizeof(*q));
q->irqlock = irqlock;
q->pci = pci;
q->type = type;
q->msize = msize;
q->ops = ops;
init_MUTEX(&q->lock);
INIT_LIST_HEAD(&q->stream);
}
void
videobuf_queue_cancel(struct file *file, struct videobuf_queue *q)
{
unsigned long flags;
int i;
/* remove queued buffers from list */
spin_lock_irqsave(q->irqlock,flags);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
if (q->bufs[i]->state == STATE_QUEUED) {
list_del(&q->bufs[i]->queue);
q->bufs[i]->state = STATE_ERROR;
}
}
spin_unlock_irqrestore(q->irqlock,flags);
/* free all buffers + clear queue */
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
q->ops->buf_release(file,q->bufs[i]);
}
INIT_LIST_HEAD(&q->stream);
}
/* --------------------------------------------------------------------- */
#ifdef HAVE_V4L2
extern void
videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb)
void
videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, int type)
{
b->index = vb->i;
b->type = vb->type;
b->type = type;
b->offset = vb->boff;
b->length = vb->bsize;
b->flags = 0;
......@@ -309,8 +359,440 @@ videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb)
b->bytesused = vb->size;
b->sequence = vb->field_count >> 1;
}
int
videobuf_reqbufs(struct file *file, struct videobuf_queue *q,
struct v4l2_requestbuffers *req)
{
int size,count,retval;
if ((req->type & V4L2_BUF_TYPE_field) != q->type)
return -EINVAL;
if (req->count < 1)
return -EINVAL;
down(&q->lock);
count = req->count;
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
size = 0;
q->ops->buf_setup(file,&count,&size);
size = PAGE_ALIGN(size);
retval = videobuf_mmap_setup(file,q,count,size);
if (retval < 0)
goto done;
req->type = q->type;
req->count = count;
done:
up(&q->lock);
return retval;
}
int
videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
{
if ((b->type & V4L2_BUF_TYPE_field) != q->type)
return -EINVAL;
if (b->index < 0 || b->index >= VIDEO_MAX_FRAME)
return -EINVAL;
if (NULL == q->bufs[b->index])
return -EINVAL;
videobuf_status(b,q->bufs[b->index],q->type);
return 0;
}
int
videobuf_qbuf(struct file *file, struct videobuf_queue *q,
struct v4l2_buffer *b)
{
struct videobuf_buffer *buf;
unsigned long flags;
int field = 0;
int retval;
down(&q->lock);
retval = -EBUSY;
if (q->reading)
goto done;
retval = -EINVAL;
if ((b->type & V4L2_BUF_TYPE_field) != q->type)
goto done;
if (b->index < 0 || b->index >= VIDEO_MAX_FRAME)
goto done;
buf = q->bufs[b->index];
if (NULL == buf)
goto done;
if (0 == buf->baddr)
goto done;
if (buf->state == STATE_QUEUED ||
buf->state == STATE_ACTIVE)
goto done;
if (b->flags & V4L2_BUF_FLAG_TOPFIELD)
field |= VBUF_FIELD_ODD;
if (b->flags & V4L2_BUF_FLAG_BOTFIELD)
field |= VBUF_FIELD_EVEN;
retval = q->ops->buf_prepare(file,buf,field);
if (0 != retval)
goto done;
list_add_tail(&buf->stream,&q->stream);
if (q->streaming) {
spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(file,buf);
spin_unlock_irqrestore(q->irqlock,flags);
}
retval = 0;
done:
up(&q->lock);
return retval;
}
int
videobuf_dqbuf(struct file *file, struct videobuf_queue *q,
struct v4l2_buffer *b)
{
struct videobuf_buffer *buf;
int retval;
down(&q->lock);
retval = -EBUSY;
if (q->reading)
goto done;
retval = -EINVAL;
if ((b->type & V4L2_BUF_TYPE_field) != q->type)
goto done;
if (list_empty(&q->stream))
goto done;
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
retval = videobuf_waiton(buf,1,1);
if (retval < 0)
goto done;
switch (buf->state) {
case STATE_ERROR:
retval = -EIO;
/* fall through */
case STATE_DONE:
videobuf_dma_pci_sync(q->pci,&buf->dma);
buf->state = STATE_IDLE;
break;
default:
retval = -EINVAL;
goto done;
}
list_del(&buf->stream);
memset(b,0,sizeof(*b));
videobuf_status(b,buf,q->type);
done:
up(&q->lock);
return retval;
}
#endif /* HAVE_V4L2 */
int videobuf_streamon(struct file *file, struct videobuf_queue *q)
{
struct videobuf_buffer *buf;
struct list_head *list;
unsigned long flags;
int retval;
down(&q->lock);
retval = -EBUSY;
if (q->reading)
goto done;
retval = 0;
if (q->streaming)
goto done;
q->streaming = 1;
spin_lock_irqsave(q->irqlock,flags);
list_for_each(list,&q->stream) {
buf = list_entry(list, struct videobuf_buffer, stream);
if (buf->state == STATE_PREPARED)
q->ops->buf_queue(file,buf);
}
spin_unlock_irqrestore(q->irqlock,flags);
done:
up(&q->lock);
return retval;
}
int videobuf_streamoff(struct file *file, struct videobuf_queue *q)
{
int retval = -EINVAL;
down(&q->lock);
if (!q->streaming)
goto done;
videobuf_queue_cancel(file,q);
q->streaming = 0;
retval = 0;
done:
up(&q->lock);
return retval;
}
static ssize_t
videobuf_read_zerocopy(struct file *file, struct videobuf_queue *q,
char *data, size_t count, loff_t *ppos)
{
int retval;
/* setup stuff */
retval = -ENOMEM;
q->read_buf = videobuf_alloc(q->msize);
if (NULL == q->read_buf)
goto done;
q->read_buf->baddr = (unsigned long)data;
q->read_buf->bsize = count;
retval = q->ops->buf_prepare(file,q->read_buf,0);
if (0 != retval)
goto done;
/* start capture & wait */
q->ops->buf_queue(file,q->read_buf);
retval = videobuf_waiton(q->read_buf,0,0);
if (0 == retval) {
videobuf_dma_pci_sync(q->pci,&q->read_buf->dma);
retval = q->read_buf->size;
}
done:
/* cleanup */
q->ops->buf_release(file,q->read_buf);
kfree(q->read_buf);
q->read_buf = NULL;
return retval;
}
ssize_t videobuf_read_one(struct file *file, struct videobuf_queue *q,
char *data, size_t count, loff_t *ppos)
{
int retval, bytes, size, nbufs;
down(&q->lock);
nbufs = 1; size = 0;
q->ops->buf_setup(file,&nbufs,&size);
if (NULL == q->read_buf &&
count >= size &&
!(file->f_flags & O_NONBLOCK)) {
retval = videobuf_read_zerocopy(file,q,data,count,ppos);
if (retval >= 0)
/* ok, all done */
goto done;
/* fallback to kernel bounce buffer on failures */
}
if (NULL == q->read_buf) {
/* need to capture a new frame */
retval = -ENOMEM;
q->read_buf = videobuf_alloc(q->msize);
if (NULL == q->read_buf)
goto done;
retval = q->ops->buf_prepare(file,q->read_buf,0);
if (0 != retval)
goto done;
q->ops->buf_queue(file,q->read_buf);
q->read_off = 0;
}
/* wait until capture is done */
retval = videobuf_waiton(q->read_buf, file->f_flags & O_NONBLOCK, 1);
if (0 != retval)
goto done;
videobuf_dma_pci_sync(q->pci,&q->read_buf->dma);
/* copy to userspace */
bytes = count;
if (bytes > q->read_buf->size - q->read_off)
bytes = q->read_buf->size - q->read_off;
retval = -EFAULT;
if (copy_to_user(data, q->read_buf->dma.vmalloc+q->read_off, bytes))
goto done;
retval = bytes;
q->read_off += bytes;
if (q->read_off == q->read_buf->size) {
/* all data copied, cleanup */
q->ops->buf_release(file,q->read_buf);
kfree(q->read_buf);
q->read_buf = NULL;
}
done:
up(&q->lock);
return retval;
}
int videobuf_read_start(struct file *file, struct videobuf_queue *q)
{
unsigned long flags;
int count = 0, size = 0;
int err, i;
q->ops->buf_setup(file,&count,&size);
if (count < 2)
count = 2;
if (count > VIDEO_MAX_FRAME)
count = VIDEO_MAX_FRAME;
size = PAGE_ALIGN(size);
err = videobuf_mmap_setup(file, q, count, size);
if (err)
return err;
for (i = 0; i < count; i++) {
err = q->ops->buf_prepare(file,q->bufs[i],0);
if (err)
return err;
list_add_tail(&q->bufs[i]->stream, &q->stream);
}
spin_lock_irqsave(q->irqlock,flags);
for (i = 0; i < count; i++)
q->ops->buf_queue(file,q->bufs[i]);
spin_unlock_irqrestore(q->irqlock,flags);
q->reading = 1;
return 0;
}
void videobuf_read_stop(struct file *file, struct videobuf_queue *q)
{
int i;
videobuf_queue_cancel(file,q);
INIT_LIST_HEAD(&q->stream);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
kfree(q->bufs[i]);
q->bufs[i] = NULL;
}
q->read_buf = NULL;
q->reading = 0;
}
ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q,
char *data, size_t count, loff_t *ppos,
int vbihack)
{
unsigned int *fc;
int err, bytes, retval;
unsigned long flags;
down(&q->lock);
retval = -EBUSY;
if (q->streaming)
goto done;
if (!q->reading) {
retval = videobuf_read_start(file,q);
if (retval < 0)
goto done;
}
retval = 0;
while (count > 0) {
/* get / wait for data */
if (NULL == q->read_buf) {
q->read_buf = list_entry(q->stream.next,
struct videobuf_buffer,
stream);
list_del(&q->read_buf->stream);
q->read_off = 0;
}
err = videobuf_waiton(q->read_buf,
file->f_flags & O_NONBLOCK,1);
if (err < 0) {
if (0 == retval)
retval = err;
break;
}
if (vbihack) {
/* 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*)q->read_buf->dma.vmalloc;
fc += (q->read_buf->size>>2) -1;
*fc = q->read_buf->field_count >> 1;
}
/* copy stuff */
bytes = count;
if (bytes > q->read_buf->size - q->read_off)
bytes = q->read_buf->size - q->read_off;
if (copy_to_user(data + retval,
q->read_buf->dma.vmalloc + q->read_off,
bytes)) {
if (0 == retval)
retval = -EFAULT;
break;
}
count -= bytes;
retval += bytes;
q->read_off += bytes;
/* requeue buffer when done with copying */
if (q->read_off == q->read_buf->size) {
list_add_tail(&q->read_buf->stream,
&q->stream);
spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(file,q->read_buf);
spin_unlock_irqrestore(q->irqlock,flags);
q->read_buf = NULL;
}
}
done:
up(&q->lock);
return retval;
}
unsigned int videobuf_poll_stream(struct file *file,
struct videobuf_queue *q,
poll_table *wait)
{
struct videobuf_buffer *buf = NULL;
unsigned int rc = 0;
down(&q->lock);
if (q->streaming) {
if (!list_empty(&q->stream))
buf = list_entry(q->stream.next,
struct videobuf_buffer, stream);
} else {
if (!q->reading)
videobuf_read_start(file,q);
if (!q->reading) {
rc = POLLERR;
} else if (NULL == q->read_buf) {
q->read_buf = list_entry(q->stream.next,
struct videobuf_buffer,
stream);
list_del(&q->read_buf->stream);
q->read_off = 0;
}
buf = q->read_buf;
}
if (!buf)
rc = POLLERR;
if (0 == rc) {
poll_wait(file, &buf->done, wait);
if (buf->state == STATE_DONE ||
buf->state == STATE_ERROR)
rc = POLLIN|POLLRDNORM;
}
up(&q->lock);
return rc;
}
/* --------------------------------------------------------------------- */
static void
......@@ -331,14 +813,15 @@ videobuf_vm_close(struct vm_area_struct *vma)
if (0 == map->count) {
dprintk(1,"munmap %p\n",map);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == map->buflist[i])
if (NULL == map->q->bufs[i])
continue;
if (map->buflist[i]->map != map)
if (map->q->bufs[i])
;
if (map->q->bufs[i]->map != map)
continue;
map->buflist[i]->map = NULL;
map->buflist[i]->baddr = 0;
if (map->buflist[i]->free)
map->buflist[i]->free(vma->vm_file,map->buflist[i]);
map->q->bufs[i]->map = NULL;
map->q->bufs[i]->baddr = 0;
map->q->ops->buf_release(vma->vm_file,map->q->bufs[i]);
}
kfree(map);
}
......@@ -376,108 +859,114 @@ static struct vm_operations_struct videobuf_vm_ops =
nopage: videobuf_vm_nopage,
};
int videobuf_mmap_setup(struct file *file, struct videobuf_buffer **buflist,
int msize, int bcount, int bsize, int type,
videobuf_buffer_free free)
int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q,
int bcount, int bsize)
{
int i,err;
err = videobuf_mmap_free(file,buflist);
err = videobuf_mmap_free(file,q);
if (0 != err)
return err;
for (i = 0; i < bcount; i++) {
buflist[i] = videobuf_alloc(msize,type);
buflist[i]->i = i;
buflist[i]->boff = bsize * i;
buflist[i]->bsize = bsize;
buflist[i]->free = free;
q->bufs[i] = videobuf_alloc(q->msize);
q->bufs[i]->i = i;
q->bufs[i]->boff = bsize * i;
q->bufs[i]->bsize = bsize;
}
dprintk(1,"mmap setup: %d buffers, %d bytes each\n",
bcount,bsize);
return 0;
}
int videobuf_mmap_free(struct file *file, struct videobuf_buffer **buflist)
int videobuf_mmap_free(struct file *file, struct videobuf_queue *q)
{
int i;
for (i = 0; i < VIDEO_MAX_FRAME; i++)
if (buflist[i] && buflist[i]->map)
if (q->bufs[i] && q->bufs[i]->map)
return -EBUSY;
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == buflist[i])
if (NULL == q->bufs[i])
continue;
if (buflist[i]->free)
buflist[i]->free(file,buflist[i]);
kfree(buflist[i]);
buflist[i] = NULL;
q->ops->buf_release(file,q->bufs[i]);
kfree(q->bufs[i]);
q->bufs[i] = NULL;
}
return 0;
}
int videobuf_mmap_mapper(struct vm_area_struct *vma,
struct videobuf_buffer **buflist)
struct videobuf_queue *q)
{
struct videobuf_mapping *map;
int first,last,size,i;
int first,last,size,i,retval;
down(&q->lock);
retval = -EINVAL;
if (!(vma->vm_flags & VM_WRITE)) {
dprintk(1,"mmap app bug: PROT_WRITE please\n");
return -EINVAL;
goto done;
}
if (!(vma->vm_flags & VM_SHARED)) {
dprintk(1,"mmap app bug: MAP_SHARED please\n");
return -EINVAL;
goto done;
}
/* look for first buffer to map */
for (first = 0; first < VIDEO_MAX_FRAME; first++) {
if (NULL == buflist[first])
if (NULL == q->bufs[first])
continue;
if (buflist[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
break;
}
if (VIDEO_MAX_FRAME == first) {
dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n",
(vma->vm_pgoff << PAGE_SHIFT));
return -EINVAL;
goto done;
}
/* look for last buffer to map */
for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
if (NULL == buflist[last])
if (NULL == q->bufs[last])
continue;
if (buflist[last]->map)
return -EBUSY;
size += buflist[last]->bsize;
if (q->bufs[last]->map) {
retval = -EBUSY;
goto done;
}
size += q->bufs[last]->bsize;
if (size == (vma->vm_end - vma->vm_start))
break;
}
if (VIDEO_MAX_FRAME == last) {
dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n",
(vma->vm_end - vma->vm_start));
return -EINVAL;
goto done;
}
/* create mapping + update buffer list */
retval = -ENOMEM;
map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL);
if (NULL == map)
return -ENOMEM;
for (size = 0, i = first; i <= last; size += buflist[i++]->bsize) {
buflist[i]->map = map;
buflist[i]->baddr = vma->vm_start + size;
goto done;
for (size = 0, i = first; i <= last; size += q->bufs[i++]->bsize) {
q->bufs[i]->map = map;
q->bufs[i]->baddr = vma->vm_start + size;
}
map->count = 1;
map->start = vma->vm_start;
map->end = vma->vm_end;
map->buflist = buflist;
map->q = q;
vma->vm_ops = &videobuf_vm_ops;
vma->vm_flags |= VM_DONTEXPAND;
vma->vm_private_data = map;
dprintk(1,"mmap %p: %08lx-%08lx pgoff %08lx bufs %d-%d\n",
map,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last);
return 0;
retval = 0;
done:
up(&q->lock);
return retval;
}
/* --------------------------------------------------------------------- */
......@@ -495,9 +984,25 @@ EXPORT_SYMBOL_GPL(videobuf_dma_free);
EXPORT_SYMBOL_GPL(videobuf_alloc);
EXPORT_SYMBOL_GPL(videobuf_waiton);
EXPORT_SYMBOL_GPL(videobuf_iolock);
EXPORT_SYMBOL_GPL(videobuf_queue_init);
EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
#ifdef HAVE_V4L2
EXPORT_SYMBOL_GPL(videobuf_status);
EXPORT_SYMBOL_GPL(videobuf_reqbufs);
EXPORT_SYMBOL_GPL(videobuf_querybuf);
EXPORT_SYMBOL_GPL(videobuf_qbuf);
EXPORT_SYMBOL_GPL(videobuf_dqbuf);
#endif
EXPORT_SYMBOL_GPL(videobuf_streamon);
EXPORT_SYMBOL_GPL(videobuf_streamoff);
EXPORT_SYMBOL_GPL(videobuf_read_start);
EXPORT_SYMBOL_GPL(videobuf_read_stop);
EXPORT_SYMBOL_GPL(videobuf_read_stream);
EXPORT_SYMBOL_GPL(videobuf_read_one);
EXPORT_SYMBOL_GPL(videobuf_poll_stream);
EXPORT_SYMBOL_GPL(videobuf_mmap_setup);
EXPORT_SYMBOL_GPL(videobuf_mmap_free);
......
......@@ -103,15 +103,14 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma);
*/
struct videobuf_buffer;
typedef void (*videobuf_buffer_free)(struct file *file,
struct videobuf_buffer *vb);
struct videobuf_queue;
struct videobuf_mapping {
int count;
int highmem_ok;
unsigned long start;
unsigned long end;
struct videobuf_buffer **buflist;
struct videobuf_queue *q;
};
#define VBUF_FIELD_EVEN 1
......@@ -132,7 +131,6 @@ struct videobuf_buffer {
int i;
/* info about the buffer */
int type;
int width;
int height;
long size;
......@@ -146,7 +144,6 @@ struct videobuf_buffer {
unsigned long bsize; /* buffer size */
unsigned long baddr; /* buffer addr (userland ptr!) */
struct videobuf_mapping *map;
videobuf_buffer_free free;
/* touched by irq handler */
struct list_head queue;
......@@ -157,19 +154,76 @@ struct videobuf_buffer {
#endif
};
void* videobuf_alloc(int size, int type);
struct videobuf_queue_ops {
int (*buf_setup)(struct file *file, int *count, int *size);
int (*buf_prepare)(struct file *file,struct videobuf_buffer *vb,
int field);
void (*buf_queue)(struct file *file,struct videobuf_buffer *vb);
void (*buf_release)(struct file *file,struct videobuf_buffer *vb);
};
struct videobuf_queue {
struct semaphore lock;
spinlock_t *irqlock;
struct pci_dev *pci;
int type;
int msize;
struct videobuf_buffer *bufs[VIDEO_MAX_FRAME];
struct videobuf_queue_ops *ops;
/* capture via mmap() + ioctl(QBUF/DQBUF) */
int streaming;
struct list_head stream;
/* capture via read() */
int reading;
int read_off;
struct videobuf_buffer *read_buf;
};
void* videobuf_alloc(int size);
int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr);
int videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb);
void videobuf_queue_init(struct videobuf_queue *q,
struct videobuf_queue_ops *ops,
struct pci_dev *pci, spinlock_t *irqlock,
int type, int msize);
void videobuf_queue_cancel(struct file *file, struct videobuf_queue *q);
#ifdef HAVE_V4L2
void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb);
void videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb,
int type);
int videobuf_reqbufs(struct file *file, struct videobuf_queue *q,
struct v4l2_requestbuffers *req);
int videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b);
int videobuf_qbuf(struct file *file, struct videobuf_queue *q,
struct v4l2_buffer *b);
int videobuf_dqbuf(struct file *file, struct videobuf_queue *q,
struct v4l2_buffer *b);
#endif
int videobuf_mmap_setup(struct file *file, struct videobuf_buffer **buflist,
int msize, int bcount, int bsize, int type,
videobuf_buffer_free free);
int videobuf_mmap_free(struct file *file, struct videobuf_buffer **buflist);
int videobuf_streamon(struct file *file, struct videobuf_queue *q);
int videobuf_streamoff(struct file *file, struct videobuf_queue *q);
int videobuf_read_start(struct file *file, struct videobuf_queue *q);
void videobuf_read_stop(struct file *file, struct videobuf_queue *q);
ssize_t videobuf_read_stream(struct file *file, struct videobuf_queue *q,
char *data, size_t count, loff_t *ppos,
int vbihack);
ssize_t videobuf_read_one(struct file *file, struct videobuf_queue *q,
char *data, size_t count, loff_t *ppos);
unsigned int videobuf_poll_stream(struct file *file,
struct videobuf_queue *q,
poll_table *wait);
int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q,
int bcount, int bsize);
int videobuf_mmap_free(struct file *file, struct videobuf_queue *q);
int videobuf_mmap_mapper(struct vm_area_struct *vma,
struct videobuf_buffer **buflist);
struct videobuf_queue *q);
/* --------------------------------------------------------------------- */
/*
* Local variables:
......
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