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); ...@@ -63,9 +63,9 @@ static void rv605_muxsel(struct bttv *btv, unsigned int input);
static int triton1=0; static int triton1=0;
static int vsfx=0; static int vsfx=0;
int no_overlay=-1; int no_overlay=-1;
static unsigned int card[4] = { -1, -1, -1, -1 }; static unsigned int card[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = -1};
static unsigned int pll[4] = { -1, -1, -1, -1 }; static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = -1};
static unsigned int tuner[4] = { -1, -1, -1, -1 }; static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = -1};
#ifdef MODULE #ifdef MODULE
static unsigned int autoload = 1; static unsigned int autoload = 1;
#else #else
...@@ -83,11 +83,11 @@ MODULE_PARM(vsfx,"i"); ...@@ -83,11 +83,11 @@ MODULE_PARM(vsfx,"i");
MODULE_PARM_DESC(vsfx,"set VSFX pci config bit " MODULE_PARM_DESC(vsfx,"set VSFX pci config bit "
"[yet another chipset flaw workaround]"); "[yet another chipset flaw workaround]");
MODULE_PARM(no_overlay,"i"); 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_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_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_DESC(tuner,"specify installed tuner type");
MODULE_PARM(autoload,"i"); MODULE_PARM(autoload,"i");
MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)"); MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
...@@ -135,9 +135,10 @@ static struct CARD { ...@@ -135,9 +135,10 @@ static struct CARD {
{ 0x00031002, BTTV_ATI_TVWONDERVE,"ATI TV Wonder/VE" }, { 0x00031002, BTTV_ATI_TVWONDERVE,"ATI TV Wonder/VE" },
{ 0x6606107d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, { 0x6606107d, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" },
{ 0x6607107d, BTTV_WINFAST2000, "Leadtek WinFast VC 100" },
{ 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" }, { 0x263610b4, BTTV_STB2, "STB TV PCI FM, P/N 6000704" },
{ 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCV3/PCI" }, { 0x402010fc, BTTV_GVBCTV3PCI, "I-O Data Co. GV-BCTV3/PCI" },
{ 0x405010fc, BTTV_GVBCTV4PCI, "I-O Data Co. GV-BCV4/PCI" }, { 0x405010fc, BTTV_GVBCTV4PCI, "I-O Data Co. GV-BCTV4/PCI" },
{ 0x1200bd11, BTTV_PINNACLE, "Pinnacle PCTV" }, { 0x1200bd11, BTTV_PINNACLE, "Pinnacle PCTV" },
{ 0x001211bd, BTTV_PINNACLE, "Pinnacle PCTV" }, { 0x001211bd, BTTV_PINNACLE, "Pinnacle PCTV" },
...@@ -163,6 +164,8 @@ static struct CARD { ...@@ -163,6 +164,8 @@ static struct CARD {
{ 0x111a153b, BTTV_TERRATVALUE, "Terratec TValue" }, { 0x111a153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x1123153b, BTTV_TERRATVRADIO, "Terratec TV Radio+" }, { 0x1123153b, BTTV_TERRATVRADIO, "Terratec TV Radio+" },
{ 0x1127153b, BTTV_TERRATV, "Terratec TV+" }, { 0x1127153b, BTTV_TERRATV, "Terratec TV+" },
// clashes with FlyVideo
//{ 0x18521852, BTTV_TERRATV, "Terratec TV+" },
{ 0x1134153b, BTTV_TERRATVALUE, "Terratec TValue" }, { 0x1134153b, BTTV_TERRATVALUE, "Terratec TValue" },
{ 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" }, { 0x1135153b, BTTV_TERRATVALUER, "Terratec TValue Radio" },
{ 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" }, { 0x5018153b, BTTV_TERRATVALUE, "Terratec TValue" },
...@@ -174,13 +177,14 @@ static struct CARD { ...@@ -174,13 +177,14 @@ static struct CARD {
{ 0x010115cb, BTTV_GMV1, "AG GMV1" }, { 0x010115cb, BTTV_GMV1, "AG GMV1" },
{ 0x010114c7, BTTV_MODTEC_205, "Modular Technology MM205 PCTV" }, { 0x010114c7, BTTV_MODTEC_205, "Modular Technology MM205 PCTV" },
{ 0x18501851, BTTV_CHRONOS_VS2, "Flyvideo 98 (LR50)/ Chronos Video Shuttle II" }, { 0x18501851, BTTV_CHRONOS_VS2, "FlyVideo 98 (LR50)/ Chronos Video Shuttle II" },
{ 0x18511851, BTTV_FLYVIDEO98EZ, "Flyvideo 98EZ (LR51)/ CyberMail AV" }, { 0x18511851, BTTV_FLYVIDEO98EZ, "FlyVideo 98EZ (LR51)/ CyberMail AV" },
{ 0x18521852, BTTV_TYPHOON_TVIEW, "Flyvideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" }, { 0x18521852, BTTV_TYPHOON_TVIEW, "FlyVideo 98FM (LR50)/ Typhoon TView TV/FM Tuner" },
{ 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" }, { 0x10b42636, BTTV_HAUPPAUGE878, "STB ???" },
{ 0x217d6606, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" }, { 0x217d6606, BTTV_WINFAST2000, "Leadtek WinFast TV 2000" },
{ 0x03116000, BTTV_SENSORAY311, "Sensoray 311" }, { 0x03116000, BTTV_SENSORAY311, "Sensoray 311" },
{ 0x00790e11, BTTV_WINDVR, "Canopus WinDVR PCI" }, { 0x00790e11, BTTV_WINDVR, "Canopus WinDVR PCI" },
{ 0xa0fca1a0, BTTV_ZOLTRIX, "Face to Face Tvmax" },
{ 0, -1, NULL } { 0, -1, NULL }
}; };
...@@ -235,16 +239,16 @@ struct tvcard bttv_tvcards[] = { ...@@ -235,16 +239,16 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x04 ---------------------------------- */ /* ---- card 0x04 ---------------------------------- */
name: "Intel", name: "Intel Create and Share PCI/ Smart Video Recorder III",
video_inputs: 3, video_inputs: 4,
audio_inputs: 1, audio_inputs: 0,
tuner: 0, tuner: -1,
svhs: -1, svhs: 2,
gpiomask: 7, gpiomask: 0,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 1, 2, 3, 4}, audiomux: { 0 },
needs_tvaudio: 1, needs_tvaudio: 0,
tuner_type: -1, tuner_type: 4,
},{ },{
name: "Diamond DTV2000", name: "Diamond DTV2000",
video_inputs: 4, video_inputs: 4,
...@@ -283,7 +287,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -283,7 +287,7 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x08 ---------------------------------- */ /* ---- card 0x08 ---------------------------------- */
name: "FlyVideo II (Bt848) LR26", name: "Lifeview FlyVideo II (Bt848) LR26",
video_inputs: 4, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -295,7 +299,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -295,7 +299,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "IXMicro TurboTV", name: "IMS/IXmicro TurboTV",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -303,8 +307,9 @@ struct tvcard bttv_tvcards[] = { ...@@ -303,8 +307,9 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 3, gpiomask: 3,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 1, 1, 2, 3, 0}, audiomux: { 1, 1, 2, 3, 0},
needs_tvaudio: 1, needs_tvaudio: 0,
tuner_type: -1, pll: PLL_28,
tuner_type: TUNER_TEMIC_PAL,
},{ },{
name: "Hauppauge (bt878)", name: "Hauppauge (bt878)",
video_inputs: 4, video_inputs: 4,
...@@ -331,7 +336,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -331,7 +336,7 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x0c ---------------------------------- */ /* ---- card 0x0c ---------------------------------- */
name: "ADS Technologies Channel Surfer TV", name: "ADS Technologies Channel Surfer TV (bt848)",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -363,6 +368,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -363,6 +368,7 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */ audiomux: { 0, 2, 1, 3, 4}, /* old: { 0, 1, 2, 3, 4} */
needs_tvaudio: 1, needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "Zoltrix TV-Max", name: "Zoltrix TV-Max",
...@@ -378,7 +384,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -378,7 +384,7 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x10 ---------------------------------- */ /* ---- card 0x10 ---------------------------------- */
name: "Pixelview PlayTV (bt878)", name: "Prolink Pixelview PlayTV (bt878)",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -414,8 +420,8 @@ struct tvcard bttv_tvcards[] = { ...@@ -414,8 +420,8 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1, needs_tvaudio: 1,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "LifeView FlyKit w/o Tuner", name: "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)",
video_inputs: 3, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: -1, tuner: -1,
svhs: -1, svhs: -1,
...@@ -435,19 +441,18 @@ struct tvcard bttv_tvcards[] = { ...@@ -435,19 +441,18 @@ struct tvcard bttv_tvcards[] = {
muxsel: {2, 3, 1, 1}, muxsel: {2, 3, 1, 1},
tuner_type: -1, tuner_type: -1,
},{ },{
name: "Lucky Star Image World ConferenceTV", name: "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50",
video_inputs: 3, video_inputs: 4,
audio_inputs: 1, audio_inputs: 2, // tuner, line in
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x00fffe07, gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 131072, 1, 1638400, 3, 4}, audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800},
needs_tvaudio: 1,
pll: PLL_28, pll: PLL_28,
tuner_type: TUNER_PHILIPS_PAL_I, tuner_type: TUNER_PHILIPS_PAL_I,
},{ },{
name: "Phoebe Tv Master + FM (CPH050)", name: "Askey CPH050/ Phoebe Tv Master + FM",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -456,6 +461,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -456,6 +461,7 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: {0, 1, 0x800, 0x400, 0xc00, 0}, audiomux: {0, 1, 0x800, 0x400, 0xc00, 0},
needs_tvaudio: 1, needs_tvaudio: 1,
pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "Modular Technology MM205 PCTV, bt878", name: "Modular Technology MM205 PCTV, bt878",
...@@ -472,7 +478,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -472,7 +478,7 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x18 ---------------------------------- */ /* ---- card 0x18 ---------------------------------- */
name: "[many vendors] CPH05X/06X (bt878)", name: "Askey CPH05X/06X (bt878) [many vendors]",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -484,7 +490,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -484,7 +490,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "Terratec/Vobis TV-Boostar", name: "Terratec Terra TV+ Version 1.0 (Bt848)/Vobis TV-Boostar",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -495,7 +501,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -495,7 +501,7 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1, needs_tvaudio: 1,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "Newer Hauppauge WinCam (bt878)", name: "Hauppauge WinCam newer (bt878)",
video_inputs: 4, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -506,15 +512,15 @@ struct tvcard bttv_tvcards[] = { ...@@ -506,15 +512,15 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1, needs_tvaudio: 1,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "MAXI TV Video PCI2", name: "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50",
video_inputs: 3, video_inputs: 4,
audio_inputs: 1, audio_inputs: 2,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0xffff, gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 1, 2, 3, 0xc00}, audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800},
needs_tvaudio: 1, pll: PLL_28,
tuner_type: TUNER_PHILIPS_SECAM, tuner_type: TUNER_PHILIPS_SECAM,
},{ },{
...@@ -543,18 +549,18 @@ struct tvcard bttv_tvcards[] = { ...@@ -543,18 +549,18 @@ struct tvcard bttv_tvcards[] = {
needs_tvaudio: 1, needs_tvaudio: 1,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "FlyVideo 98", name: "Lifeview FlyVideo 98 LR50",
video_inputs: 3, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x1800, //0x8dfe00 gpiomask: 0x1800, //0x8dfe00
muxsel: {2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x0800, 0x1000, 0x1000, 0x1800, 0 }, audiomux: { 0, 0x0800, 0x1000, 0x1000, 0x1800, 0 },
needs_tvaudio: 1, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "iProTV", name: "Formac iProTV",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -566,16 +572,16 @@ struct tvcard bttv_tvcards[] = { ...@@ -566,16 +572,16 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x20 ---------------------------------- */ /* ---- card 0x20 ---------------------------------- */
name: "Intel Create and Share PCI", name: "Intel Create and Share PCI/ Smart Video Recorder III",
video_inputs: 4, video_inputs: 4,
audio_inputs: 1, audio_inputs: 0,
tuner: 0, tuner: -1,
svhs: 2, svhs: 2,
gpiomask: 7, gpiomask: 0,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 4, 4, 4, 4, 4}, audiomux: { 0 },
needs_tvaudio: 1, needs_tvaudio: 0,
tuner_type: -1, tuner_type: 4,
},{ },{
name: "Terratec TerraTValue", name: "Terratec TerraTValue",
video_inputs: 3, video_inputs: 3,
...@@ -602,34 +608,32 @@ struct tvcard bttv_tvcards[] = { ...@@ -602,34 +608,32 @@ struct tvcard bttv_tvcards[] = {
tuner_type: -1, tuner_type: -1,
audio_hook: winfast2000_audio, audio_hook: winfast2000_audio,
},{ },{
name: "Flyvideo 98 (LR50Q) / Chronos Video Shuttle II", name: "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
video_inputs: 3, video_inputs: 4,
audio_inputs: 3, audio_inputs: 3,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x1800, gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800}, audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800},
needs_tvaudio: 1,
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
/* ---- card 0x24 ---------------------------------- */ /* ---- card 0x24 ---------------------------------- */
name: "Flyvideo 98FM (LR50Q) / Typhoon TView TV/FM Tuner", name: "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner",
video_inputs: 3, video_inputs: 4,
audio_inputs: 3, audio_inputs: 3,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x1800, gpiomask: 0x1800,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 }, audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 },
needs_tvaudio: 1,
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
has_radio: 1, has_radio: 1,
},{ },{
name: "PixelView PlayTV pro", name: "Prolink PixelView PlayTV pro",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -641,7 +645,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -641,7 +645,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "TView99 CPH06X", name: "Askey CPH06X TView99",
video_inputs: 4, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -684,7 +688,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -684,7 +688,7 @@ struct tvcard bttv_tvcards[] = {
audio_inputs: 4, audio_inputs: 4,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 12, gpiomask: 15,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 13, 4, 11, 7, 0, 0}, audiomux: { 13, 4, 11, 7, 0, 0},
needs_tvaudio: 1, needs_tvaudio: 1,
...@@ -700,6 +704,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -700,6 +704,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0, gpiomask: 0,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0, 0, 0, 0}, audiomux: { 0, 0, 0, 0, 0},
needs_tvaudio: 1,
no_msp34xx: 1, no_msp34xx: 1,
pll: PLL_28, pll: PLL_28,
tuner_type: 1, tuner_type: 1,
...@@ -758,9 +763,9 @@ struct tvcard bttv_tvcards[] = { ...@@ -758,9 +763,9 @@ struct tvcard bttv_tvcards[] = {
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x1f0000, gpiomask: 0x70000,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0xe2ffff, 0xebffff, 0, 0, 0xe0ffff, 0xe2ffff }, audiomux: { 0x20000, 0x30000, 0x10000, 0, 0x40000, 0x20000 },
needs_tvaudio: 1, needs_tvaudio: 1,
no_msp34xx: 1, no_msp34xx: 1,
pll: PLL_35, pll: PLL_35,
...@@ -769,7 +774,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -769,7 +774,7 @@ struct tvcard bttv_tvcards[] = {
},{ },{
/* ---- card 0x30 ---------------------------------- */ /* ---- card 0x30 ---------------------------------- */
name: "Dynalink Magic TView ", name: "Askey CPH03x/ Dynalink Magic TView",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -781,7 +786,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -781,7 +786,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
name: "GV-BCTV3", name: "IODATA GV-BCTV3/PCI",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -828,6 +833,15 @@ struct tvcard bttv_tvcards[] = { ...@@ -828,6 +833,15 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x03000F, gpiomask: 0x03000F,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 1, 0x10001, 0, 0, 10}, 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, needs_tvaudio: 1,
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
...@@ -852,7 +866,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -852,7 +866,7 @@ struct tvcard bttv_tvcards[] = {
options bttv card=0 pll=1 radio=1 gpiomask=0x18e0 options bttv card=0 pll=1 radio=1 gpiomask=0x18e0
audiomux=0x44c71f,0x44d71f,0,0x44d71f,0x44dfff audiomux=0x44c71f,0x44d71f,0,0x44d71f,0x44dfff
options tuner type=5 */ 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, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -860,17 +874,16 @@ struct tvcard bttv_tvcards[] = { ...@@ -860,17 +874,16 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x18e0, gpiomask: 0x18e0,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0x0000,0x0800,0x1000,0x1000,0x18e0 }, 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) 0x0080: Tuner A2 SAP (second audio program = Zweikanalton)
0x0880: Tuner A2 stereo */ 0x0880: Tuner A2 stereo */
pll: PLL_28, pll: PLL_28,
tuner_type: TUNER_PHILIPS_PAL, tuner_type: -1,
audio_hook: lt9415_audio,
has_radio: 1,
},{ },{
/* Miguel Angel Alvarez <maacruz@navegalia.com> /* Miguel Angel Alvarez <maacruz@navegalia.com>
old Easy TV BT848 version (model CPH031) */ old Easy TV BT848 version (model CPH031) */
name: "BESTBUY Easy TV (CPH031)", name: "Askey CPH031/ BESTBUY Easy TV",
video_inputs: 4, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -885,15 +898,14 @@ struct tvcard bttv_tvcards[] = { ...@@ -885,15 +898,14 @@ struct tvcard bttv_tvcards[] = {
/* ---- card 0x38 ---------------------------------- */ /* ---- card 0x38 ---------------------------------- */
/* Gordon Heydon <gjheydon@bigfoot.com ('98) */ /* Gordon Heydon <gjheydon@bigfoot.com ('98) */
name: "FlyVideo '98/FM", name: "Lifeview FlyVideo 98FM LR50",
video_inputs: 3, video_inputs: 4,
audio_inputs: 3, audio_inputs: 3,
tuner: 0, tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x1800, gpiomask: 0x1800,
muxsel: { 2, 3, 0, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 }, audiomux: { 0, 0x800, 0x1000, 0x1000, 0x1800, 0 },
needs_tvaudio: 1,
pll: PLL_28, pll: PLL_28,
tuner_type: 5, tuner_type: 5,
},{ },{
...@@ -914,7 +926,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -914,7 +926,7 @@ struct tvcard bttv_tvcards[] = {
tuner_type: -1, tuner_type: -1,
},{ },{
/* Daniel Herrington <daniel.herrington@home.com> */ /* 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, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -923,11 +935,11 @@ struct tvcard bttv_tvcards[] = { ...@@ -923,11 +935,11 @@ struct tvcard bttv_tvcards[] = {
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 0x400, 0x400, 0x400, 0x400, 0x800, 0x400 }, audiomux: { 0x400, 0x400, 0x400, 0x400, 0x800, 0x400 },
needs_tvaudio: 1, needs_tvaudio: 1,
pll: PLL_NONE, pll: PLL_28,
tuner_type: TUNER_TEMIC_4036FY5_NTSC, tuner_type: TUNER_TEMIC_4036FY5_NTSC,
},{ },{
/* Matti Mottus <mottus@physic.ut.ee> */ /* Matti Mottus <mottus@physic.ut.ee> */
name: "TV Capturer (CPH03X)", name: "Askey CPH03x TV Capturer",
video_inputs: 4, video_inputs: 4,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -944,10 +956,12 @@ struct tvcard bttv_tvcards[] = { ...@@ -944,10 +956,12 @@ struct tvcard bttv_tvcards[] = {
name: "Modular Technology MM100PCTV", name: "Modular Technology MM100PCTV",
video_inputs: 2, video_inputs: 2,
audio_inputs: 2, audio_inputs: 2,
tuner: 0,
svhs: -1,
gpiomask: 11, gpiomask: 11,
muxsel: { 2, 3, 1, 1}, muxsel: { 2, 3, 1, 1},
audiomux: { 2, 0, 0, 1, 8}, audiomux: { 2, 0, 0, 1, 8},
pll: PLL_NONE, pll: PLL_35,
tuner_type: TUNER_TEMIC_PAL, tuner_type: TUNER_TEMIC_PAL,
},{ },{
...@@ -968,7 +982,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -968,7 +982,7 @@ struct tvcard bttv_tvcards[] = {
/* Miguel Angel Alvarez <maacruz@navegalia.com> /* Miguel Angel Alvarez <maacruz@navegalia.com>
new Easy TV BT878 version (model CPH061) new Easy TV BT878 version (model CPH061)
special thanks to Informatica Mieres for providing the card */ special thanks to Informatica Mieres for providing the card */
name: "BESTBUY Easy TV (bt878)", name: "Askey CPH061/ BESTBUY Easy TV (bt878)",
video_inputs: 3, video_inputs: 3,
audio_inputs: 2, audio_inputs: 2,
tuner: 0, tuner: 0,
...@@ -1008,7 +1022,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1008,7 +1022,7 @@ struct tvcard bttv_tvcards[] = {
tuner_type: TUNER_TEMIC_4006FN5_MULTI_PAL, tuner_type: TUNER_TEMIC_4006FN5_MULTI_PAL,
},{ },{
/* DeeJay <deejay@westel900.net (2000S) */ /* DeeJay <deejay@westel900.net (2000S) */
name: "FlyVideo 2000S", name: "Lifeview FlyVideo 2000S LR90",
video_inputs: 3, video_inputs: 3,
audio_inputs: 3, audio_inputs: 3,
tuner: 0, tuner: 0,
...@@ -1016,7 +1030,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1016,7 +1030,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0x18e0, gpiomask: 0x18e0,
muxsel: { 2, 3, 0, 1}, muxsel: { 2, 3, 0, 1},
/* Radio changed from 1e80 to 0x800 to make /* 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 */ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */
audiomux: { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 }, audiomux: { 0x0000,0x0800,0x1000,0x1000,0x1800, 0x1080 },
audio_hook: fv2000s_audio, audio_hook: fv2000s_audio,
...@@ -1040,7 +1054,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1040,7 +1054,7 @@ struct tvcard bttv_tvcards[] = {
has_radio: 1, has_radio: 1,
},{ },{
/* TANAKA Kei <peg00625@nifty.com> */ /* TANAKA Kei <peg00625@nifty.com> */
name: "GV-BCTV4/PCI", name: "IODATA GV-BCTV4/PCI",
video_inputs: 3, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -1084,7 +1098,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1084,7 +1098,7 @@ struct tvcard bttv_tvcards[] = {
gpiomask: 0 gpiomask: 0
},{ },{
/* Tomasz Pyra <hellfire@sedez.iq.pl> */ /* Tomasz Pyra <hellfire@sedez.iq.pl> */
name: "PV-BT878P+", name: "Prolink Pixelview PV-BT878P+ (Rev.4C)",
video_inputs: 3, video_inputs: 3,
audio_inputs: 4, audio_inputs: 4,
tuner: 0, tuner: 0,
...@@ -1096,7 +1110,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1096,7 +1110,7 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28, pll: PLL_28,
tuner_type: 25, tuner_type: 25,
},{ },{
name: "Flyvideo 98EZ (capture only)", name: "Lifeview FlyVideo 98EZ (capture only) LR51",
video_inputs: 4, video_inputs: 4,
audio_inputs: 0, audio_inputs: 0,
tuner: -1, tuner: -1,
...@@ -1108,7 +1122,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1108,7 +1122,7 @@ struct tvcard bttv_tvcards[] = {
/* ---- card 0x48 ---------------------------------- */ /* ---- card 0x48 ---------------------------------- */
/* Dariusz Kowalewski <darekk@automex.pl> */ /* 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, video_inputs: 3,
audio_inputs: 1, audio_inputs: 1,
tuner: 0, tuner: 0,
...@@ -1141,8 +1155,8 @@ struct tvcard bttv_tvcards[] = { ...@@ -1141,8 +1155,8 @@ struct tvcard bttv_tvcards[] = {
name: "RemoteVision MX (RV605)", name: "RemoteVision MX (RV605)",
video_inputs: 16, video_inputs: 16,
audio_inputs: 0, audio_inputs: 0,
tuner: 0, tuner: -1,
svhs: 0, svhs: -1,
gpiomask: 0x00, gpiomask: 0x00,
gpiomask2: 0x07ff, gpiomask2: 0x07ff,
muxsel: { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03, muxsel: { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03,
...@@ -1155,6 +1169,7 @@ struct tvcard bttv_tvcards[] = { ...@@ -1155,6 +1169,7 @@ struct tvcard bttv_tvcards[] = {
name: "Powercolor MTV878/ MTV878R/ MTV878F", name: "Powercolor MTV878/ MTV878R/ MTV878F",
video_inputs: 3, video_inputs: 3,
audio_inputs: 2, audio_inputs: 2,
tuner: 0,
svhs: 2, svhs: 2,
gpiomask: 0x1C800F, // Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset gpiomask: 0x1C800F, // Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset
muxsel: { 2, 1, 1, }, muxsel: { 2, 1, 1, },
...@@ -1191,15 +1206,42 @@ struct tvcard bttv_tvcards[] = { ...@@ -1191,15 +1206,42 @@ struct tvcard bttv_tvcards[] = {
pll: PLL_28, pll: PLL_28,
tuner_type: -1, tuner_type: -1,
},{ },{
/* http://www.aopen.com/products/video/va1000.htm */ name: "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF",
name: "AOPEN VA1000", video_inputs: 4,
video_inputs: 3, /* coax, AV, s-vid */ audio_inputs: 3,
audio_inputs: 1, tuner: 0,
tuner: 0, svhs: 2,
tuner_type: TUNER_LG_PAL, /* actually TP18PSB12P (PAL B/G) */ gpiomask: 7,
audiomux: { 2, 0, 0, 0 }, muxsel: { 2, 3, 1, 1 }, // Tuner, SVid, SVHS, SVid to SVHS connector
muxsel: { 2, 3, 1, 0 }, audiomux: { 0 ,0 ,4, 4,4,4},// Yes, this tuner uses the same audio output for TV and FM radio!
pll: PLL_28, // 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)); const int bttv_num_tvcards = (sizeof(bttv_tvcards)/sizeof(struct tvcard));
...@@ -1289,8 +1331,8 @@ void __devinit bttv_idcard(struct bttv *btv) ...@@ -1289,8 +1331,8 @@ void __devinit bttv_idcard(struct bttv *btv)
*/ */
static void flyvideo_gpio(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; int tuner=-1,ttype;
outbits = btread(BT848_GPIO_OUT_EN); outbits = btread(BT848_GPIO_OUT_EN);
...@@ -1299,29 +1341,54 @@ static void flyvideo_gpio(struct bttv *btv) ...@@ -1299,29 +1341,54 @@ static void flyvideo_gpio(struct bttv *btv)
gpio=btread(BT848_GPIO_DATA); gpio=btread(BT848_GPIO_DATA);
btwrite(outbits, BT848_GPIO_OUT_EN); btwrite(outbits, BT848_GPIO_OUT_EN);
// all cards provide GPIO info, some have an additional eeprom // 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) // 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; ttype=(gpio&0x0f0000)>>16;
switch(ttype) { switch(ttype) {
case 0: tuner=4; // None case 0x0: tuner=4; // None
break; break;
case 4: tuner=5; // Philips PAL case 0x4: tuner=5; // Philips PAL
break; break;
case 6: tuner=37; // LG PAL (newer TAPC series) case 0x6: tuner=37; // LG PAL (newer TAPC series)
break; break;
case 0xC: tuner=3; // Philips SECAM(+PAL) case 0xC: tuner=3; // Philips SECAM(+PAL)
break; break;
default: 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", has_remote = gpio & 0x800000;
btv->nr, has_radio = gpio & 0x400000;
gpio&0x400000? "yes":"no", // unknown 0x200000;
gpio&0x800000? "yes":"no", tuner, gpio); // unknown2 0x100000;
is_capture_only = !(gpio & 0x008000); //GPIO15
btv->tuner_type = tuner; has_tda9820_tda9821 = !(gpio & 0x004000);
btv->has_radio = gpio&0x400000? 1:0; 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, 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, ...@@ -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, 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 }; 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 */ /* initialization part one -- before registering i2c bus */
void __devinit bttv_init_card1(struct bttv *btv) void __devinit bttv_init_card1(struct bttv *btv)
{ {
if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878)
boot_msp34xx(btv,5); boot_msp34xx(btv,5);
if (btv->type == BTTV_VOODOOTV_FM) if (btv->type == BTTV_VOODOOTV_FM)
boot_msp34xx(btv,20); 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) void __devinit bttv_init_card2(struct bttv *btv)
{ {
/* miro/pinnacle */ btv->tuner_type = -1;
/* miro/pinnacle */
if (btv->type == BTTV_MIRO || if (btv->type == BTTV_MIRO ||
btv->type == BTTV_MIROPRO || btv->type == BTTV_MIROPRO ||
btv->type == BTTV_PINNACLE || btv->type == BTTV_PINNACLE ||
btv->type == BTTV_PINNACLEPRO) { btv->type == BTTV_PINNACLEPRO)
int id,msp; miro_pinnacle_gpio(btv);
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
}
if (btv->type == BTTV_FLYVIDEO_98 || if (btv->type == BTTV_FLYVIDEO_98 ||
btv->type == BTTV_LIFE_FLYKIT ||
btv->type == BTTV_FLYVIDEO || btv->type == BTTV_FLYVIDEO ||
btv->type == BTTV_TYPHOON_TVIEW || btv->type == BTTV_TYPHOON_TVIEW ||
btv->type == BTTV_CHRONOS_VS2 || btv->type == BTTV_CHRONOS_VS2 ||
btv->type == BTTV_FLYVIDEO_98FM || btv->type == BTTV_FLYVIDEO_98FM ||
btv->type == BTTV_FLYVIDEO2000 || btv->type == BTTV_FLYVIDEO2000 ||
btv->type == BTTV_FLYVIDEO98EZ) btv->type == BTTV_FLYVIDEO98EZ ||
btv->type == BTTV_CONFERENCETV ||
btv->type == BTTV_LIFETEC_9415)
flyvideo_gpio(btv); flyvideo_gpio(btv);
if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) { if (btv->type == BTTV_HAUPPAUGE || btv->type == BTTV_HAUPPAUGE878) {
...@@ -1418,13 +1486,6 @@ void __devinit bttv_init_card2(struct bttv *btv) ...@@ -1418,13 +1486,6 @@ void __devinit bttv_init_card2(struct bttv *btv)
btv->mbox_mask = 0x38; 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->type == BTTV_MAGICTVIEW061) {
if(btv->cardid == 0x4002144f) { if(btv->cardid == 0x4002144f) {
btv->has_radio=1; btv->has_radio=1;
...@@ -1465,17 +1526,22 @@ void __devinit bttv_init_card2(struct bttv *btv) ...@@ -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) 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]) if (-1 != tuner[btv->nr])
btv->tuner_type = tuner[btv->nr]; btv->tuner_type = tuner[btv->nr];
if (btv->tuner_type != -1) if (btv->tuner_type != -1)
bttv_call_i2c_clients(btv,TUNER_SET_TYPE,&btv->tuner_type); 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) if (bttv_tvcards[btv->type].has_radio)
btv->has_radio=1; 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 */ /* try to detect audio/fader chips */
if (!bttv_tvcards[btv->type].no_msp34xx && if (!bttv_tvcards[btv->type].no_msp34xx &&
bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) { bttv_I2CRead(btv, I2C_MSP3400, "MSP34xx") >=0) {
......
...@@ -80,7 +80,7 @@ static unsigned int v4l2 = 1; ...@@ -80,7 +80,7 @@ static unsigned int v4l2 = 1;
/* insmod args */ /* 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_DESC(radio,"The TV card supports radio, default is 0 (no)");
MODULE_PARM(bigendian,"i"); MODULE_PARM(bigendian,"i");
MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); 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) ...@@ -762,7 +762,7 @@ video_mux(struct bttv *btv, unsigned int input)
btand(~(3<<5), BT848_IFORM); btand(~(3<<5), BT848_IFORM);
schedule_timeout(HZ/10); schedule_timeout(HZ/10);
#endif #endif
if (input==bttv_tvcards[btv->type].svhs) { if (input==bttv_tvcards[btv->type].svhs) {
btor(BT848_CONTROL_COMP, BT848_E_CONTROL); btor(BT848_CONTROL_COMP, BT848_E_CONTROL);
btor(BT848_CONTROL_COMP, BT848_O_CONTROL); btor(BT848_CONTROL_COMP, BT848_O_CONTROL);
...@@ -789,10 +789,11 @@ static char *audio_modes[] = { ...@@ -789,10 +789,11 @@ static char *audio_modes[] = {
static int static int
audio_mux(struct bttv *btv, int mode) audio_mux(struct bttv *btv, int mode)
{ {
int val,mux; int val,mux,i2c_mux,signal;
btaor(bttv_tvcards[btv->type].gpiomask, btaor(bttv_tvcards[btv->type].gpiomask,
~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN); ~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN);
signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC;
switch (mode) { switch (mode) {
case AUDIO_MUTE: case AUDIO_MUTE:
...@@ -808,16 +809,19 @@ audio_mux(struct bttv *btv, int mode) ...@@ -808,16 +809,19 @@ audio_mux(struct bttv *btv, int mode)
btv->audio &= AUDIO_MUTE; btv->audio &= AUDIO_MUTE;
btv->audio |= mode; btv->audio |= mode;
} }
dprintk("bttv%d: audio mux: mode=%d audio=%d\n", i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio;
btv->nr,mode,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]; val = bttv_tvcards[btv->type].audiomux[mux];
btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA);
if (bttv_gpio) if (bttv_gpio)
bttv_gpio_tracking(btv,audio_modes[mux]); bttv_gpio_tracking(btv,audio_modes[mux]);
if (!in_interrupt()) if (!in_interrupt())
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(mux)); bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux));
return 0; return 0;
} }
...@@ -830,6 +834,8 @@ i2c_vidiocschan(struct bttv *btv) ...@@ -830,6 +834,8 @@ i2c_vidiocschan(struct bttv *btv)
c.norm = btv->tvnorm; c.norm = btv->tvnorm;
c.channel = btv->input; c.channel = btv->input;
bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c); bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c);
if (btv->type == BTTV_VOODOOTV_FM)
bttv_tda9880_setnorm(btv,c.norm);
} }
static int static int
...@@ -931,8 +937,8 @@ static int get_control(struct bttv *btv, struct v4l2_control *c) ...@@ -931,8 +937,8 @@ static int get_control(struct bttv *btv, struct v4l2_control *c)
if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
memset(&va,0,sizeof(va)); memset(&va,0,sizeof(va));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0); btv->audio_hook(btv,&va,0);
} }
switch (c->id) { switch (c->id) {
case V4L2_CID_BRIGHTNESS: case V4L2_CID_BRIGHTNESS:
...@@ -998,8 +1004,8 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) ...@@ -998,8 +1004,8 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
memset(&va,0,sizeof(va)); memset(&va,0,sizeof(va));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0); btv->audio_hook(btv,&va,0);
} }
switch (c->id) { switch (c->id) {
case V4L2_CID_BRIGHTNESS: case V4L2_CID_BRIGHTNESS:
...@@ -1069,8 +1075,8 @@ static int set_control(struct bttv *btv, struct v4l2_control *c) ...@@ -1069,8 +1075,8 @@ static int set_control(struct bttv *btv, struct v4l2_control *c)
} }
if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) { if (bttv_ctls[i].category == V4L2_CTRL_CAT_AUDIO) {
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,1); btv->audio_hook(btv,&va,1);
} }
return 0; return 0;
} }
...@@ -1161,35 +1167,6 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, ...@@ -1161,35 +1167,6 @@ bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh,
return retval; 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 */ /* video4linux (1) interface */
...@@ -1218,26 +1195,28 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, ...@@ -1218,26 +1195,28 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
buf->vb.size = (width * height * fmt->depth) >> 3; buf->vb.size = (width * height * fmt->depth) >> 3;
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
return -EINVAL; return -EINVAL;
field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN,
btv->tvnorm, height);
} }
/* alloc + fill struct bttv_buffer (if changed) */ /* alloc + fill struct bttv_buffer (if changed) */
if (buf->vb.width != width || buf->vb.height != height || if (buf->vb.width != width || buf->vb.height != height ||
buf->vb.field != field ||
buf->tvnorm != btv->tvnorm || buf->fmt != fmt) { buf->tvnorm != btv->tvnorm || buf->fmt != fmt) {
buf->vb.width = width; buf->vb.width = width;
buf->vb.height = height; buf->vb.height = height;
buf->vb.field = field;
buf->tvnorm = btv->tvnorm; buf->tvnorm = btv->tvnorm;
buf->fmt = fmt; buf->fmt = fmt;
redo_dma_risc = 1; redo_dma_risc = 1;
} }
if (STATE_NEEDS_INIT == buf->vb.state) { #if 0
field = bttv_buffer_field(btv, field, VBUF_FIELD_EVEN, if (STATE_NEEDS_INIT == buf->vb.state)
btv->tvnorm, height);
if (field != buf->vb.field)
redo_dma_risc = 1;
if (redo_dma_risc) if (redo_dma_risc)
bttv_dma_free(btv,buf); bttv_dma_free(btv,buf);
} }
#endif
/* alloc risc memory */ /* alloc risc memory */
if (STATE_NEEDS_INIT == buf->vb.state) { if (STATE_NEEDS_INIT == buf->vb.state) {
...@@ -1258,18 +1237,55 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, ...@@ -1258,18 +1237,55 @@ static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf,
return rc; return rc;
} }
static void static int
bttv_queue_buffer(struct bttv *btv, struct bttv_buffer *buf) 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; buf->vb.state = STATE_QUEUED;
spin_lock_irqsave(&btv->s_lock,flags); list_add_tail(&buf->vb.queue,&fh->btv->capture);
list_add_tail(&buf->vb.queue,&btv->capture); bttv_set_dma(fh->btv, 0x03, 1);
bttv_set_dma(btv, 0x03, 1);
spin_unlock_irqrestore(&btv->s_lock,flags);
} }
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[] = { static const char *v4l1_ioctls[] = {
"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT",
"CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ",
...@@ -1413,8 +1429,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) ...@@ -1413,8 +1429,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
bttv_call_i2c_clients(btv,cmd,v); bttv_call_i2c_clients(btv,cmd,v);
/* card specific hooks */ /* card specific hooks */
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,v,0); btv->audio_hook(btv,v,0);
up(&btv->lock); up(&btv->lock);
return 0; return 0;
...@@ -1432,8 +1448,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) ...@@ -1432,8 +1448,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
bttv_call_i2c_clients(btv,cmd,v); bttv_call_i2c_clients(btv,cmd,v);
/* card specific hooks */ /* card specific hooks */
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,v,1); btv->audio_hook(btv,v,1);
up(&btv->lock); up(&btv->lock);
return 0; return 0;
...@@ -1533,8 +1549,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) ...@@ -1533,8 +1549,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
struct video_audio va; struct video_audio va;
memset(&va, 0, sizeof(struct video_audio)); memset(&va, 0, sizeof(struct video_audio));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0); btv->audio_hook(btv,&va,0);
if(va.mode & VIDEO_SOUND_STEREO) if(va.mode & VIDEO_SOUND_STEREO)
t->rxsubchans |= V4L2_TUNER_SUB_STEREO; t->rxsubchans |= V4L2_TUNER_SUB_STEREO;
if(va.mode & VIDEO_SOUND_LANG1) if(va.mode & VIDEO_SOUND_LANG1)
...@@ -1564,8 +1580,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) ...@@ -1564,8 +1580,8 @@ int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg)
else if (t->audmode == V4L2_TUNER_MODE_LANG2) else if (t->audmode == V4L2_TUNER_MODE_LANG2)
va.mode = VIDEO_SOUND_LANG2; va.mode = VIDEO_SOUND_LANG2;
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,1); btv->audio_hook(btv,&va,1);
} }
up(&btv->lock); up(&btv->lock);
return 0; return 0;
...@@ -1616,7 +1632,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, ...@@ -1616,7 +1632,7 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
clips, n); clips, n);
bttv_sort_clips(clips,nclips); bttv_sort_clips(clips,nclips);
down(&fh->lock); down(&fh->q.lock);
if (fh->ov.clips) if (fh->ov.clips)
kfree(fh->ov.clips); kfree(fh->ov.clips);
fh->ov.clips = clips; fh->ov.clips = clips;
...@@ -1634,27 +1650,20 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv, ...@@ -1634,27 +1650,20 @@ static int setup_window(struct bttv_fh *fh, struct bttv *btv,
if (check_btres(fh, RESOURCE_OVERLAY)) { if (check_btres(fh, RESOURCE_OVERLAY)) {
struct bttv_buffer *new; 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); bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
retval = bttv_switch_overlay(btv,fh,new); retval = bttv_switch_overlay(btv,fh,new);
} }
up(&fh->lock); up(&fh->q.lock);
return retval; 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, static int bttv_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg) unsigned int cmd, void *arg)
{ {
struct bttv_fh *fh = file->private_data; struct bttv_fh *fh = file->private_data;
struct bttv *btv = fh->btv; struct bttv *btv = fh->btv;
unsigned long flags;
int retval; int retval;
if (bttv_debug > 1) { if (bttv_debug > 1) {
...@@ -1723,7 +1732,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1723,7 +1732,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
fmt = format_by_palette(pic->palette); fmt = format_by_palette(pic->palette);
if (NULL == fmt) if (NULL == fmt)
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
retval = -EINVAL; retval = -EINVAL;
if (fmt->depth != pic->depth && !sloppy) if (fmt->depth != pic->depth && !sloppy)
goto fh_unlock_and_return; goto fh_unlock_and_return;
...@@ -1744,7 +1753,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1744,7 +1753,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
bt848_contrast(btv,pic->contrast); bt848_contrast(btv,pic->contrast);
bt848_hue(btv,pic->hue); bt848_hue(btv,pic->hue);
bt848_sat(btv,pic->colour); bt848_sat(btv,pic->colour);
up(&fh->lock); up(&fh->q.lock);
return 0; return 0;
} }
...@@ -1796,7 +1805,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1796,7 +1805,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
fbuf->height * fbuf->bytesperline; fbuf->height * fbuf->bytesperline;
if (0 == find_videomem((unsigned long)fbuf->base,end)) if (0 == find_videomem((unsigned long)fbuf->base,end))
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
retval = -EINVAL; retval = -EINVAL;
if (sloppy) { if (sloppy) {
/* also set the default palette -- for backward /* also set the default palette -- for backward
...@@ -1836,7 +1845,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1836,7 +1845,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
goto fh_unlock_and_return; goto fh_unlock_and_return;
} }
btv->fbuf = *fbuf; btv->fbuf = *fbuf;
up(&fh->lock); up(&fh->q.lock);
return 0; return 0;
} }
...@@ -1863,11 +1872,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1863,11 +1872,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY)) if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY))
return -EBUSY; return -EBUSY;
down(&fh->lock); down(&fh->q.lock);
if (*on) { if (*on) {
fh->ov.tvnorm = btv->tvnorm; fh->ov.tvnorm = btv->tvnorm;
new = videobuf_alloc(sizeof(*new), new = videobuf_alloc(sizeof(*new));
V4L2_BUF_TYPE_CAPTURE);
bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new);
} else { } else {
new = NULL; new = NULL;
...@@ -1875,7 +1883,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1875,7 +1883,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
/* switch over */ /* switch over */
retval = bttv_switch_overlay(btv,fh,new); retval = bttv_switch_overlay(btv,fh,new);
up(&fh->lock); up(&fh->q.lock);
return retval; return retval;
} }
...@@ -1886,12 +1894,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1886,12 +1894,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (!mmap) if (!mmap)
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
retval = videobuf_mmap_setup retval = videobuf_mmap_setup(file,&fh->q,gbuffers,gbufsize);
(file,(struct videobuf_buffer**)fh->bufs,
sizeof(struct bttv_buffer),
gbuffers,gbufsize,V4L2_BUF_TYPE_CAPTURE,
release_buffer);
if (retval < 0) if (retval < 0)
goto fh_unlock_and_return; goto fh_unlock_and_return;
memset(mbuf,0,sizeof(*mbuf)); memset(mbuf,0,sizeof(*mbuf));
...@@ -1899,7 +1903,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1899,7 +1903,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
mbuf->size = gbuffers * gbufsize; mbuf->size = gbuffers * gbufsize;
for (i = 0; i < gbuffers; i++) for (i = 0; i < gbuffers; i++)
mbuf->offsets[i] = i * gbufsize; mbuf->offsets[i] = i * gbufsize;
up(&fh->lock); up(&fh->q.lock);
return 0; return 0;
} }
case VIDIOCMCAPTURE: case VIDIOCMCAPTURE:
...@@ -1910,9 +1914,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1910,9 +1914,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (vm->frame >= VIDEO_MAX_FRAME) if (vm->frame >= VIDEO_MAX_FRAME)
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
retval = -EINVAL; retval = -EINVAL;
buf = fh->bufs[vm->frame]; buf = (struct bttv_buffer *)fh->q.bufs[vm->frame];
if (NULL == buf) if (NULL == buf)
goto fh_unlock_and_return; goto fh_unlock_and_return;
if (0 == buf->vb.baddr) if (0 == buf->vb.baddr)
...@@ -1926,8 +1930,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1926,8 +1930,10 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
vm->width,vm->height,0); vm->width,vm->height,0);
if (0 != retval) if (0 != retval)
goto fh_unlock_and_return; goto fh_unlock_and_return;
bttv_queue_buffer(btv,buf); spin_lock_irqsave(&btv->s_lock,flags);
up(&fh->lock); buffer_queue(file,&buf->vb);
spin_unlock_irqrestore(&btv->s_lock,flags);
up(&fh->q.lock);
return 0; return 0;
} }
case VIDIOCSYNC: case VIDIOCSYNC:
...@@ -1938,9 +1944,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1938,9 +1944,9 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (*frame >= VIDEO_MAX_FRAME) if (*frame >= VIDEO_MAX_FRAME)
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
retval = -EINVAL; retval = -EINVAL;
buf = fh->bufs[*frame]; buf = (struct bttv_buffer *)fh->q.bufs[*frame];
if (NULL == buf) if (NULL == buf)
goto fh_unlock_and_return; goto fh_unlock_and_return;
retval = videobuf_waiton(&buf->vb,0,1); retval = videobuf_waiton(&buf->vb,0,1);
...@@ -1958,7 +1964,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -1958,7 +1964,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
retval = -EINVAL; retval = -EINVAL;
break; break;
} }
up(&fh->lock); up(&fh->q.lock);
return retval; return retval;
} }
...@@ -2056,7 +2062,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -2056,7 +2062,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
/* FIXME -- not implemented yet */ /* FIXME -- not implemented yet */
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
/* fixup format */ /* fixup format */
if (f->fmt.pix.width > bttv_tvnorms[btv->tvnorm].swidth) if (f->fmt.pix.width > bttv_tvnorms[btv->tvnorm].swidth)
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, ...@@ -2087,7 +2093,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
f->fmt.pix.depth = fmt->depth; f->fmt.pix.depth = fmt->depth;
f->fmt.pix.sizeimage = f->fmt.pix.sizeimage =
(fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8; (fh->buf.vb.width * fh->buf.vb.height * fmt->depth)/8;
up(&fh->lock); up(&fh->q.lock);
return 0; return 0;
} }
...@@ -2130,7 +2136,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -2130,7 +2136,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) if (0 == (fmt->flags & FORMAT_FLAGS_PACKED))
return -EINVAL; return -EINVAL;
down(&fh->lock); down(&fh->q.lock);
retval = -EINVAL; retval = -EINVAL;
if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) {
if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth)
...@@ -2167,13 +2173,12 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -2167,13 +2173,12 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
if (check_btres(fh, RESOURCE_OVERLAY)) { if (check_btres(fh, RESOURCE_OVERLAY)) {
struct bttv_buffer *new; struct bttv_buffer *new;
new = videobuf_alloc(sizeof(*new), new = videobuf_alloc(sizeof(*new));
V4L2_BUF_TYPE_CAPTURE);
bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new); bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new);
retval = bttv_switch_overlay(btv,fh,new); retval = bttv_switch_overlay(btv,fh,new);
} }
} }
up(&fh->lock); up(&fh->q.lock);
return retval; return retval;
} }
case VIDIOC_G_WIN: case VIDIOC_G_WIN:
...@@ -2198,154 +2203,32 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -2198,154 +2203,32 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
} }
case VIDIOC_REQBUFS: case VIDIOC_REQBUFS:
{
struct v4l2_requestbuffers *req = arg;
int size,count;
if (!mmap) if (!mmap)
return -EINVAL; return -EINVAL;
if ((req->type & V4L2_BUF_TYPE_field) != V4L2_BUF_TYPE_CAPTURE) return videobuf_reqbufs(file,&fh->q,arg);
return -EINVAL;
if (req->count < 0)
return -EINVAL;
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: case VIDIOC_QUERYBUF:
{ return videobuf_querybuf(&fh->q,arg);
struct v4l2_buffer *b = 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: case VIDIOC_QBUF:
{ return videobuf_qbuf(file,&fh->q,arg);
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;
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: case VIDIOC_DQBUF:
{ return videobuf_dqbuf(file,&fh->q,arg);
struct v4l2_buffer *b = arg;
struct bttv_buffer *buf;
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: case VIDIOC_STREAMON:
{
struct list_head *list;
struct bttv_buffer *buf;
if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING)) if (!check_alloc_btres(btv,fh,RESOURCE_STREAMING))
return -EBUSY; return -EBUSY;
bttv_field_count(btv); bttv_field_count(btv);
down(&fh->lock); return videobuf_streamon(file,&fh->q);
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;
}
case VIDIOC_STREAMOFF: case VIDIOC_STREAMOFF:
{ retval = videobuf_streamoff(file,&fh->q);
down(&fh->lock); if (retval < 0)
retval = -EINVAL; return retval;
if (!check_btres(fh, RESOURCE_STREAMING)) free_btres(btv,fh,RESOURCE_STREAMING);
goto fh_unlock_and_return; bttv_field_count(btv);
bttv_stop_streaming(btv,fh);
up(&fh->lock);
return 0; return 0;
}
case VIDIOC_QUERYCTRL: case VIDIOC_QUERYCTRL:
{ {
...@@ -2370,8 +2253,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -2370,8 +2253,8 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
struct video_audio va; struct video_audio va;
memset(&va,0,sizeof(va)); memset(&va,0,sizeof(va));
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va);
if (bttv_tvcards[btv->type].audio_hook) if (btv->audio_hook)
bttv_tvcards[btv->type].audio_hook(btv,&va,0); btv->audio_hook(btv,&va,0);
switch (bttv_ctls[i].id) { switch (bttv_ctls[i].id) {
case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_VOLUME:
if (!(va.flags & VIDEO_AUDIO_VOLUME)) if (!(va.flags & VIDEO_AUDIO_VOLUME))
...@@ -2428,7 +2311,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file, ...@@ -2428,7 +2311,7 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
return 0; return 0;
fh_unlock_and_return: fh_unlock_and_return:
up(&fh->lock); up(&fh->q.lock);
return retval; return retval;
} }
...@@ -2438,23 +2321,7 @@ static int bttv_ioctl(struct inode *inode, struct file *file, ...@@ -2438,23 +2321,7 @@ static int bttv_ioctl(struct inode *inode, struct file *file,
return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl); return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl);
} }
/* start capture to a kernel bounce buffer */ #if 0
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;
}
/* /*
* blocking read for a complete video frame * blocking read for a complete video frame
* => no kernel bounce buffer needed. * => no kernel bounce buffer needed.
...@@ -2467,87 +2334,42 @@ bttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv, ...@@ -2467,87 +2334,42 @@ bttv_read_zerocopy(struct bttv_fh *fh, struct bttv *btv,
/* setup stuff */ /* setup stuff */
dprintk("bttv%d: read zerocopy\n",btv->nr); dprintk("bttv%d: read zerocopy\n",btv->nr);
fh->read_buf.vb.baddr = (unsigned long)data; fh->q.read_buf->baddr = (unsigned long)data;
fh->read_buf.vb.bsize = count; fh->q.read_buf->bsize = count;
rc = bttv_prepare_buffer(btv,&fh->read_buf,fh->buf.fmt, rc = bttv_prepare_buffer(btv,fh->q.read_buf,fh->buf.fmt,
fh->buf.vb.width,fh->buf.vb.height,0); fh->buf.vb.width,fh->buf.vb.height,0);
if (0 != rc) if (0 != rc)
goto done; goto done;
/* start capture & wait */ /* start capture & wait */
bttv_queue_buffer(btv,&fh->read_buf); bttv_queue_buffer(btv,fh->q.read_buf);
rc = videobuf_waiton(&fh->read_buf.vb,0,1); rc = videobuf_waiton(fh->q.read_buf,0,1);
if (0 == rc) { if (0 == rc) {
videobuf_dma_pci_sync(btv->dev,&fh->read_buf.vb.dma); videobuf_dma_pci_sync(btv->dev,&fh->q.read_buf->dma);
rc = fh->read_buf.vb.size; rc = fh->q.read_buf->size;
} }
done: done:
/* cleanup */ /* cleanup */
bttv_dma_free(btv,&fh->read_buf); bttv_dma_free(btv,fh->q.read_buf);
fh->read_buf.vb.baddr = 0; fh->q.read_buf->baddr = 0;
fh->read_buf.vb.size = 0; fh->q.read_buf->size = 0;
return rc; return rc;
} }
#endif
static ssize_t bttv_read(struct file *file, char *data, static ssize_t bttv_read(struct file *file, char *data,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
struct bttv_fh *fh = file->private_data; struct bttv_fh *fh = file->private_data;
struct bttv *btv = fh->btv;
int rc, bytes, size;
if (btv->errors) if (fh->btv->errors)
bttv_reinit_bt848(btv); bttv_reinit_bt848(fh->btv);
if (locked_btres(btv,RESOURCE_STREAMING)) if (locked_btres(fh->btv,RESOURCE_STREAMING))
return -EBUSY; return -EBUSY;
down(&fh->lock); return videobuf_read_one(file, &fh->q, data, count, ppos);
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;
} }
static unsigned int bttv_poll(struct file *file, poll_table *wait) 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) ...@@ -2555,24 +2377,34 @@ static unsigned int bttv_poll(struct file *file, poll_table *wait)
struct bttv_fh *fh = file->private_data; struct bttv_fh *fh = file->private_data;
struct bttv_buffer *buf; struct bttv_buffer *buf;
if (check_alloc_btres(fh->btv,fh,RESOURCE_STREAMING)) { if (check_btres(fh,RESOURCE_STREAMING)) {
/* streaming capture */ /* streaming capture */
if (list_empty(&fh->stream)) if (list_empty(&fh->q.stream))
return POLLERR; 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 { } else {
/* read() capture */ /* read() capture */
down(&fh->lock); down(&fh->q.lock);
if (-1 == fh->read_off) { if (NULL == fh->q.read_buf) {
/* need to capture a new frame */ /* need to capture a new frame */
if (locked_btres(fh->btv,RESOURCE_STREAMING) || if (locked_btres(fh->btv,RESOURCE_STREAMING)) {
0 != bttv_read_capture(fh)) { up(&fh->q.lock);
up(&fh->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; return POLLERR;
} }
fh->q.ops->buf_queue(file,fh->q.read_buf);
fh->q.read_off = 0;
} }
up(&fh->lock); up(&fh->q.lock);
buf = &fh->read_buf; buf = (struct bttv_buffer*)fh->q.read_buf;
} }
poll_wait(file, &buf->vb.done, wait); poll_wait(file, &buf->vb.done, wait);
...@@ -2608,11 +2440,9 @@ static int bttv_open(struct inode *inode, struct file *file) ...@@ -2608,11 +2440,9 @@ static int bttv_open(struct inode *inode, struct file *file)
return -ENOMEM; return -ENOMEM;
file->private_data = fh; file->private_data = fh;
*fh = btv->init; *fh = btv->init;
init_MUTEX(&fh->lock); videobuf_queue_init(&fh->q, &bttv_qops, btv->dev, &btv->s_lock,
INIT_LIST_HEAD(&fh->stream); V4L2_BUF_TYPE_CAPTURE,sizeof(struct bttv_buffer));
init_waitqueue_head(&fh->read_buf.vb.done);
fh->read_off = -1;
i2c_vidiocschan(btv); i2c_vidiocschan(btv);
return 0; return 0;
} }
...@@ -2625,9 +2455,15 @@ static int bttv_release(struct inode *inode, struct file *file) ...@@ -2625,9 +2455,15 @@ static int bttv_release(struct inode *inode, struct file *file)
/* turn off overlay, stop outstanding captures */ /* turn off overlay, stop outstanding captures */
if (check_btres(fh, RESOURCE_OVERLAY)) if (check_btres(fh, RESOURCE_OVERLAY))
bttv_switch_overlay(btv,fh,NULL); bttv_switch_overlay(btv,fh,NULL);
if (check_btres(fh, RESOURCE_STREAMING)) if (check_btres(fh, RESOURCE_STREAMING)) {
bttv_stop_streaming(btv,fh); videobuf_streamoff(file,&fh->q);
bttv_dma_free(btv,&fh->read_buf); 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; file->private_data = NULL;
kfree(fh); kfree(fh);
...@@ -2638,14 +2474,12 @@ static int ...@@ -2638,14 +2474,12 @@ static int
bttv_mmap(struct file *file, struct vm_area_struct *vma) bttv_mmap(struct file *file, struct vm_area_struct *vma)
{ {
struct bttv_fh *fh = file->private_data; struct bttv_fh *fh = file->private_data;
int err;
if (!mmap) if (!mmap)
return -EINVAL; return -EINVAL;
down(&fh->lock); dprintk("mmap 0x%lx+%ld\n",vma->vm_start,
err = videobuf_mmap_mapper(vma,(struct videobuf_buffer**)fh->bufs); vma->vm_end - vma->vm_start);
up(&fh->lock); return videobuf_mmap_mapper(vma,&fh->q);
return err;
} }
static struct file_operations bttv_fops = static struct file_operations bttv_fops =
...@@ -2786,7 +2620,7 @@ static struct file_operations radio_fops = ...@@ -2786,7 +2620,7 @@ static struct file_operations radio_fops =
static struct video_device radio_template = static struct video_device radio_template =
{ {
name: "bt848/878 radio", name: "bt848/878 radio",
type: VID_TYPE_TUNER|VID_TYPE_TELETEXT, type: VID_TYPE_TUNER,
hardware: VID_HARDWARE_BT848, hardware: VID_HARDWARE_BT848,
fops: &radio_fops, fops: &radio_fops,
minor: -1, minor: -1,
...@@ -3075,12 +2909,8 @@ static void bttv_irq(int irq, void *dev_id, struct pt_regs * regs) ...@@ -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))) if ((astat & BT848_INT_RISCI) && (stat & (1<<28)))
bttv_irq_switch_fields(btv); bttv_irq_switch_fields(btv);
if ((astat & BT848_INT_HLOCK) && btv->opt_automute) { if ((astat & BT848_INT_HLOCK) && btv->opt_automute)
if ((dstat & BT848_DSTATUS_HLOC) || btv->radio_user) audio_mux(btv, -1);
audio_mux(btv, AUDIO_UNMUTE);
else
audio_mux(btv, AUDIO_MUTE);
}
if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) {
printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->nr, printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->nr,
...@@ -3175,9 +3005,8 @@ static int __devinit bttv_probe(struct pci_dev *dev, ...@@ -3175,9 +3005,8 @@ static int __devinit bttv_probe(struct pci_dev *dev,
init_waitqueue_head(&btv->gpioq); init_waitqueue_head(&btv->gpioq);
INIT_LIST_HEAD(&btv->capture); INIT_LIST_HEAD(&btv->capture);
INIT_LIST_HEAD(&btv->vcapture); INIT_LIST_HEAD(&btv->vcapture);
videobuf_queue_init(&btv->vbi.q, &vbi_qops, btv->dev, &btv->s_lock,
init_MUTEX(&btv->vbi.lock); V4L2_BUF_TYPE_VBI,sizeof(struct bttv_buffer));
INIT_LIST_HEAD(&btv->vbi.stream);
btv->timeout.function = bttv_irq_timeout; btv->timeout.function = bttv_irq_timeout;
btv->timeout.data = (unsigned long)btv; btv->timeout.data = (unsigned long)btv;
......
...@@ -69,8 +69,21 @@ vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf) ...@@ -69,8 +69,21 @@ vbi_buffer_risc(struct bttv *btv, struct bttv_buffer *buf)
return 0; 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; int rc;
buf->vb.size = btv->vbi.lines * 2 * 2048; buf->vb.size = btv->vbi.lines * 2 * 2048;
...@@ -84,7 +97,8 @@ static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf) ...@@ -84,7 +97,8 @@ static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
goto fail; goto fail;
} }
buf->vb.state = STATE_PREPARED; 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; return 0;
fail: fail:
...@@ -93,94 +107,35 @@ static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf) ...@@ -93,94 +107,35 @@ static int vbi_buffer_prepare(struct bttv *btv, struct bttv_buffer *buf)
} }
static void 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; buf->vb.state = STATE_QUEUED;
spin_lock_irqsave(&btv->s_lock,flags);
list_add_tail(&buf->vb.queue,&btv->vcapture); list_add_tail(&buf->vb.queue,&btv->vcapture);
bttv_set_dma(btv,0x0c,1); 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) static void vbi_buffer_release(struct file *file, struct videobuf_buffer *vb)
{ {
struct bttv *btv = file->private_data; struct bttv *btv = file->private_data;
struct bttv_buffer *buf = (struct bttv_buffer*)vb; struct bttv_buffer *buf = (struct bttv_buffer*)vb;
dprintk("free %p\n",vb);
bttv_dma_free(btv,buf); bttv_dma_free(btv,buf);
} }
static void struct videobuf_queue_ops vbi_qops = {
vbi_cancel_all(struct bttv *btv) buf_setup: vbi_buffer_setup,
{ buf_prepare: vbi_buffer_prepare,
unsigned long flags; buf_queue: vbi_buffer_queue,
int i; buf_release: vbi_buffer_release,
};
/* 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) static void vbi_setlines(struct bttv *btv, int lines)
{ {
int vdelay; int vdelay;
...@@ -234,7 +189,6 @@ static int vbi_open(struct inode *inode, struct file *file) ...@@ -234,7 +189,6 @@ static int vbi_open(struct inode *inode, struct file *file)
struct bttv *btv = NULL; struct bttv *btv = NULL;
int i; int i;
for (i = 0; i < bttv_num; i++) { for (i = 0; i < bttv_num; i++) {
if (bttvs[i].vbi_dev.minor == minor) { if (bttvs[i].vbi_dev.minor == minor) {
btv = &bttvs[i]; btv = &bttvs[i];
...@@ -244,18 +198,18 @@ static int vbi_open(struct inode *inode, struct file *file) ...@@ -244,18 +198,18 @@ static int vbi_open(struct inode *inode, struct file *file)
if (NULL == btv) if (NULL == btv)
return -ENODEV; return -ENODEV;
down(&btv->vbi.lock); down(&btv->vbi.q.lock);
if (btv->vbi.users) { if (btv->vbi.users) {
up(&btv->vbi.lock); up(&btv->vbi.q.lock);
return -EBUSY; return -EBUSY;
} }
dprintk("open minor=%d\n",minor); dprintk("open minor=%d\n",minor);
file->private_data = btv; file->private_data = btv;
btv->vbi.users++; btv->vbi.users++;
bttv_field_count(btv);
vbi_setlines(btv,VBI_DEFLINES); vbi_setlines(btv,VBI_DEFLINES);
bttv_field_count(btv);
up(&btv->vbi.lock); up(&btv->vbi.q.lock);
return 0; return 0;
} }
...@@ -264,15 +218,15 @@ static int vbi_release(struct inode *inode, struct file *file) ...@@ -264,15 +218,15 @@ static int vbi_release(struct inode *inode, struct file *file)
{ {
struct bttv *btv = file->private_data; struct bttv *btv = file->private_data;
down(&btv->vbi.lock); if (btv->vbi.q.streaming)
if (btv->vbi.reading) { videobuf_streamoff(file,&btv->vbi.q);
vbi_read_stop(btv); down(&btv->vbi.q.lock);
btv->vbi.read_buf = NULL; if (btv->vbi.q.reading)
} videobuf_read_stop(file,&btv->vbi.q);
btv->vbi.users--; btv->vbi.users--;
bttv_field_count(btv); bttv_field_count(btv);
vbi_setlines(btv,0); vbi_setlines(btv,0);
up(&btv->vbi.lock); up(&btv->vbi.q.lock);
return 0; return 0;
} }
...@@ -280,10 +234,6 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file, ...@@ -280,10 +234,6 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg) unsigned int cmd, void *arg)
{ {
struct bttv *btv = file->private_data; struct bttv *btv = file->private_data;
#ifdef HAVE_V4L2
unsigned long flags;
int err;
#endif
if (btv->errors) if (btv->errors)
bttv_reinit_bt848(btv); bttv_reinit_bt848(btv);
...@@ -334,7 +284,7 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file, ...@@ -334,7 +284,7 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
{ {
struct v4l2_format *f = arg; struct v4l2_format *f = arg;
if (btv->vbi.reading || btv->vbi.streaming) if (btv->vbi.q.reading || btv->vbi.q.streaming)
return -EBUSY; return -EBUSY;
vbi_setlines(btv,f->fmt.vbi.count[0]); vbi_setlines(btv,f->fmt.vbi.count[0]);
vbi_fmt(btv,f); vbi_fmt(btv,f);
...@@ -342,145 +292,22 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file, ...@@ -342,145 +292,22 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
} }
case VIDIOC_REQBUFS: case VIDIOC_REQBUFS:
{ return videobuf_reqbufs(file,&btv->vbi.q,arg);
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: case VIDIOC_QUERYBUF:
{ return videobuf_querybuf(&btv->vbi.q, arg);
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: case VIDIOC_QBUF:
{ return videobuf_qbuf(file, &btv->vbi.q, arg);
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: case VIDIOC_DQBUF:
{ return videobuf_dqbuf(file, &btv->vbi.q, arg);
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: case VIDIOC_STREAMON:
{ return videobuf_streamon(file, &btv->vbi.q);
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: case VIDIOC_STREAMOFF:
{ return videobuf_streamoff(file, &btv->vbi.q);
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_ENUMSTD:
case VIDIOC_G_STD: case VIDIOC_G_STD:
...@@ -499,12 +326,6 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file, ...@@ -499,12 +326,6 @@ static int vbi_do_ioctl(struct inode *inode, struct file *file,
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
return 0; 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, static int vbi_ioctl(struct inode *inode, struct file *file,
...@@ -516,123 +337,30 @@ 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, static ssize_t vbi_read(struct file *file, char *data,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
unsigned int *fc;
struct bttv *btv = file->private_data; struct bttv *btv = file->private_data;
int err, bytes, retval = 0;
if (btv->errors) if (btv->errors)
bttv_reinit_bt848(btv); bttv_reinit_bt848(btv);
down(&btv->vbi.lock); dprintk("read %d\n",count);
if (!btv->vbi.reading) { return videobuf_read_stream(file, &btv->vbi.q, data, count, ppos, 1);
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) static unsigned int vbi_poll(struct file *file, poll_table *wait)
{ {
struct bttv *btv = file->private_data; struct bttv *btv = file->private_data;
struct bttv_buffer *buf = NULL;
unsigned int rc = 0; dprintk("poll%s\n","");
return videobuf_poll_stream(file, &btv->vbi.q, wait);
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 static int
vbi_mmap(struct file *file, struct vm_area_struct * vma) vbi_mmap(struct file *file, struct vm_area_struct * vma)
{ {
struct bttv *btv = file->private_data; struct bttv *btv = file->private_data;
int err;
dprintk("mmap 0x%lx+%ld\n",vma->vm_start,
down(&btv->vbi.lock); vma->vm_end - vma->vm_start);
err = videobuf_mmap_mapper return videobuf_mmap_mapper(vma, &btv->vbi.q);
(vma,(struct videobuf_buffer**)btv->vbi.bufs);
up(&btv->vbi.lock);
return err;
} }
static struct file_operations vbi_fops = static struct file_operations vbi_fops =
......
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
#ifndef _BTTVP_H_ #ifndef _BTTVP_H_
#define _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/types.h>
#include <linux/wait.h> #include <linux/wait.h>
...@@ -127,40 +127,20 @@ struct bttv_overlay { ...@@ -127,40 +127,20 @@ struct bttv_overlay {
}; };
struct bttv_vbi { struct bttv_vbi {
struct semaphore lock; int users;
int users; int lines;
int lines; struct videobuf_queue q;
/* 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_fh {
struct bttv *btv; struct bttv *btv;
struct videobuf_queue q;
/* locking */
int resources; int resources;
struct semaphore lock;
/* current settings */
/* keep current driver settings */
const struct bttv_format *ovfmt; const struct bttv_format *ovfmt;
struct bttv_overlay ov; struct bttv_overlay ov;
struct bttv_buffer buf; 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, ...@@ -221,6 +201,8 @@ int bttv_overlay_risc(struct bttv *btv, struct bttv_overlay *ov,
/* bttv-vbi.c */ /* bttv-vbi.c */
extern struct video_device bttv_vbi_template; extern struct video_device bttv_vbi_template;
extern struct videobuf_queue_ops vbi_qops;
/* ---------------------------------------------------------- */ /* ---------------------------------------------------------- */
/* bttv-driver.c */ /* bttv-driver.c */
...@@ -277,7 +259,8 @@ struct bttv { ...@@ -277,7 +259,8 @@ struct bttv {
/* gpio interface */ /* gpio interface */
wait_queue_head_t gpioq; wait_queue_head_t gpioq;
int shutdown; int shutdown;
void (*audio_hook)(struct bttv *btv, struct video_audio *v, int set);
/* i2c layer */ /* i2c layer */
struct i2c_adapter i2c_adap; struct i2c_adapter i2c_adap;
struct i2c_algo_bit_data i2c_algo; struct i2c_algo_bit_data i2c_algo;
......
...@@ -207,14 +207,13 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) ...@@ -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; struct videobuf_buffer *vb;
vb = kmalloc(size,GFP_KERNEL); vb = kmalloc(size,GFP_KERNEL);
if (NULL != vb) { if (NULL != vb) {
memset(vb,0,size); memset(vb,0,size);
vb->type = type;
init_waitqueue_head(&vb->done); init_waitqueue_head(&vb->done);
} }
return vb; return vb;
...@@ -273,12 +272,63 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb) ...@@ -273,12 +272,63 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb)
return 0; 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 #ifdef HAVE_V4L2
extern void void
videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb) videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb, int type)
{ {
b->index = vb->i; b->index = vb->i;
b->type = vb->type; b->type = type;
b->offset = vb->boff; b->offset = vb->boff;
b->length = vb->bsize; b->length = vb->bsize;
b->flags = 0; b->flags = 0;
...@@ -309,8 +359,440 @@ videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb) ...@@ -309,8 +359,440 @@ videobuf_status(struct v4l2_buffer *b, struct videobuf_buffer *vb)
b->bytesused = vb->size; b->bytesused = vb->size;
b->sequence = vb->field_count >> 1; 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 */ #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 static void
...@@ -331,14 +813,15 @@ videobuf_vm_close(struct vm_area_struct *vma) ...@@ -331,14 +813,15 @@ videobuf_vm_close(struct vm_area_struct *vma)
if (0 == map->count) { if (0 == map->count) {
dprintk(1,"munmap %p\n",map); dprintk(1,"munmap %p\n",map);
for (i = 0; i < VIDEO_MAX_FRAME; i++) { for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == map->buflist[i]) if (NULL == map->q->bufs[i])
continue; continue;
if (map->buflist[i]->map != map) if (map->q->bufs[i])
;
if (map->q->bufs[i]->map != map)
continue; continue;
map->buflist[i]->map = NULL; map->q->bufs[i]->map = NULL;
map->buflist[i]->baddr = 0; map->q->bufs[i]->baddr = 0;
if (map->buflist[i]->free) map->q->ops->buf_release(vma->vm_file,map->q->bufs[i]);
map->buflist[i]->free(vma->vm_file,map->buflist[i]);
} }
kfree(map); kfree(map);
} }
...@@ -376,108 +859,114 @@ static struct vm_operations_struct videobuf_vm_ops = ...@@ -376,108 +859,114 @@ static struct vm_operations_struct videobuf_vm_ops =
nopage: videobuf_vm_nopage, nopage: videobuf_vm_nopage,
}; };
int videobuf_mmap_setup(struct file *file, struct videobuf_buffer **buflist, int videobuf_mmap_setup(struct file *file, struct videobuf_queue *q,
int msize, int bcount, int bsize, int type, int bcount, int bsize)
videobuf_buffer_free free)
{ {
int i,err; int i,err;
err = videobuf_mmap_free(file,buflist); err = videobuf_mmap_free(file,q);
if (0 != err) if (0 != err)
return err; return err;
for (i = 0; i < bcount; i++) { for (i = 0; i < bcount; i++) {
buflist[i] = videobuf_alloc(msize,type); q->bufs[i] = videobuf_alloc(q->msize);
buflist[i]->i = i; q->bufs[i]->i = i;
buflist[i]->boff = bsize * i; q->bufs[i]->boff = bsize * i;
buflist[i]->bsize = bsize; q->bufs[i]->bsize = bsize;
buflist[i]->free = free;
} }
dprintk(1,"mmap setup: %d buffers, %d bytes each\n", dprintk(1,"mmap setup: %d buffers, %d bytes each\n",
bcount,bsize); bcount,bsize);
return 0; 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; int i;
for (i = 0; i < VIDEO_MAX_FRAME; 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; return -EBUSY;
for (i = 0; i < VIDEO_MAX_FRAME; i++) { for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == buflist[i]) if (NULL == q->bufs[i])
continue; continue;
if (buflist[i]->free) q->ops->buf_release(file,q->bufs[i]);
buflist[i]->free(file,buflist[i]); kfree(q->bufs[i]);
kfree(buflist[i]); q->bufs[i] = NULL;
buflist[i] = NULL;
} }
return 0; return 0;
} }
int videobuf_mmap_mapper(struct vm_area_struct *vma, int videobuf_mmap_mapper(struct vm_area_struct *vma,
struct videobuf_buffer **buflist) struct videobuf_queue *q)
{ {
struct videobuf_mapping *map; 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)) { if (!(vma->vm_flags & VM_WRITE)) {
dprintk(1,"mmap app bug: PROT_WRITE please\n"); dprintk(1,"mmap app bug: PROT_WRITE please\n");
return -EINVAL; goto done;
} }
if (!(vma->vm_flags & VM_SHARED)) { if (!(vma->vm_flags & VM_SHARED)) {
dprintk(1,"mmap app bug: MAP_SHARED please\n"); dprintk(1,"mmap app bug: MAP_SHARED please\n");
return -EINVAL; goto done;
} }
/* look for first buffer to map */ /* look for first buffer to map */
for (first = 0; first < VIDEO_MAX_FRAME; first++) { for (first = 0; first < VIDEO_MAX_FRAME; first++) {
if (NULL == buflist[first]) if (NULL == q->bufs[first])
continue; continue;
if (buflist[first]->boff == (vma->vm_pgoff << PAGE_SHIFT)) if (q->bufs[first]->boff == (vma->vm_pgoff << PAGE_SHIFT))
break; break;
} }
if (VIDEO_MAX_FRAME == first) { if (VIDEO_MAX_FRAME == first) {
dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n", dprintk(1,"mmap app bug: offset invalid [offset=0x%lx]\n",
(vma->vm_pgoff << PAGE_SHIFT)); (vma->vm_pgoff << PAGE_SHIFT));
return -EINVAL; goto done;
} }
/* look for last buffer to map */ /* look for last buffer to map */
for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) { for (size = 0, last = first; last < VIDEO_MAX_FRAME; last++) {
if (NULL == buflist[last]) if (NULL == q->bufs[last])
continue; continue;
if (buflist[last]->map) if (q->bufs[last]->map) {
return -EBUSY; retval = -EBUSY;
size += buflist[last]->bsize; goto done;
}
size += q->bufs[last]->bsize;
if (size == (vma->vm_end - vma->vm_start)) if (size == (vma->vm_end - vma->vm_start))
break; break;
} }
if (VIDEO_MAX_FRAME == last) { if (VIDEO_MAX_FRAME == last) {
dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n", dprintk(1,"mmap app bug: size invalid [size=0x%lx]\n",
(vma->vm_end - vma->vm_start)); (vma->vm_end - vma->vm_start));
return -EINVAL; goto done;
} }
/* create mapping + update buffer list */ /* create mapping + update buffer list */
retval = -ENOMEM;
map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL); map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL);
if (NULL == map) if (NULL == map)
return -ENOMEM; goto done;
for (size = 0, i = first; i <= last; size += buflist[i++]->bsize) { for (size = 0, i = first; i <= last; size += q->bufs[i++]->bsize) {
buflist[i]->map = map; q->bufs[i]->map = map;
buflist[i]->baddr = vma->vm_start + size; q->bufs[i]->baddr = vma->vm_start + size;
} }
map->count = 1; map->count = 1;
map->start = vma->vm_start; map->start = vma->vm_start;
map->end = vma->vm_end; map->end = vma->vm_end;
map->buflist = buflist; map->q = q;
vma->vm_ops = &videobuf_vm_ops; vma->vm_ops = &videobuf_vm_ops;
vma->vm_flags |= VM_DONTEXPAND; vma->vm_flags |= VM_DONTEXPAND;
vma->vm_private_data = map; vma->vm_private_data = map;
dprintk(1,"mmap %p: %08lx-%08lx pgoff %08lx bufs %d-%d\n", dprintk(1,"mmap %p: %08lx-%08lx pgoff %08lx bufs %d-%d\n",
map,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last); 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); ...@@ -495,9 +984,25 @@ EXPORT_SYMBOL_GPL(videobuf_dma_free);
EXPORT_SYMBOL_GPL(videobuf_alloc); EXPORT_SYMBOL_GPL(videobuf_alloc);
EXPORT_SYMBOL_GPL(videobuf_waiton); EXPORT_SYMBOL_GPL(videobuf_waiton);
EXPORT_SYMBOL_GPL(videobuf_iolock); EXPORT_SYMBOL_GPL(videobuf_iolock);
EXPORT_SYMBOL_GPL(videobuf_queue_init);
EXPORT_SYMBOL_GPL(videobuf_queue_cancel);
#ifdef HAVE_V4L2 #ifdef HAVE_V4L2
EXPORT_SYMBOL_GPL(videobuf_status); 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 #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_setup);
EXPORT_SYMBOL_GPL(videobuf_mmap_free); EXPORT_SYMBOL_GPL(videobuf_mmap_free);
......
...@@ -103,15 +103,14 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma); ...@@ -103,15 +103,14 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma);
*/ */
struct videobuf_buffer; struct videobuf_buffer;
typedef void (*videobuf_buffer_free)(struct file *file, struct videobuf_queue;
struct videobuf_buffer *vb);
struct videobuf_mapping { struct videobuf_mapping {
int count; int count;
int highmem_ok; int highmem_ok;
unsigned long start; unsigned long start;
unsigned long end; unsigned long end;
struct videobuf_buffer **buflist; struct videobuf_queue *q;
}; };
#define VBUF_FIELD_EVEN 1 #define VBUF_FIELD_EVEN 1
...@@ -132,7 +131,6 @@ struct videobuf_buffer { ...@@ -132,7 +131,6 @@ struct videobuf_buffer {
int i; int i;
/* info about the buffer */ /* info about the buffer */
int type;
int width; int width;
int height; int height;
long size; long size;
...@@ -146,7 +144,6 @@ struct videobuf_buffer { ...@@ -146,7 +144,6 @@ struct videobuf_buffer {
unsigned long bsize; /* buffer size */ unsigned long bsize; /* buffer size */
unsigned long baddr; /* buffer addr (userland ptr!) */ unsigned long baddr; /* buffer addr (userland ptr!) */
struct videobuf_mapping *map; struct videobuf_mapping *map;
videobuf_buffer_free free;
/* touched by irq handler */ /* touched by irq handler */
struct list_head queue; struct list_head queue;
...@@ -157,19 +154,76 @@ struct videobuf_buffer { ...@@ -157,19 +154,76 @@ struct videobuf_buffer {
#endif #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_waiton(struct videobuf_buffer *vb, int non_blocking, int intr);
int videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb); 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 #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 #endif
int videobuf_streamon(struct file *file, struct videobuf_queue *q);
int videobuf_mmap_setup(struct file *file, struct videobuf_buffer **buflist, int videobuf_streamoff(struct file *file, struct videobuf_queue *q);
int msize, int bcount, int bsize, int type,
videobuf_buffer_free free); int videobuf_read_start(struct file *file, struct videobuf_queue *q);
int videobuf_mmap_free(struct file *file, struct videobuf_buffer **buflist); 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, int videobuf_mmap_mapper(struct vm_area_struct *vma,
struct videobuf_buffer **buflist); struct videobuf_queue *q);
/* --------------------------------------------------------------------- */
/* /*
* Local variables: * 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