1,170 → 1,662 |
/* |
bttv - Bt848 frame grabber driver |
|
Copyright (C) 1996,97,98 Ralph Metzler (rjkm@thp.uni-koeln.de) |
& Marcus Metzler (mocm@thp.uni-koeln.de) |
(c) 1999,2000 Gerd Knorr <kraxel@goldbach.in-berlin.de> |
|
|
Copyright (C) 1996,97,98 Ralph Metzler <rjkm@thp.uni-koeln.de> |
& Marcus Metzler <mocm@thp.uni-koeln.de> |
(c) 1999-2002 Gerd Knorr <kraxel@bytesex.org> |
|
some v4l2 code lines are taken from Justin's bttv2 driver which is |
(c) 2000 Justin Schoeman <justin@suntiger.ee.up.ac.za> |
|
This program is free software; you can redistribute it and/or modify |
it under the terms of the GNU General Public License as published by |
the Free Software Foundation; either version 2 of the License, or |
(at your option) any later version. |
|
|
This program is distributed in the hope that it will be useful, |
but WITHOUT ANY WARRANTY; without even the implied warranty of |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
GNU General Public License for more details. |
|
|
You should have received a copy of the GNU General Public License |
along with this program; if not, write to the Free Software |
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
*/ |
|
/* SHARK version by Giacomo Guidi <giacomo@gandalf.sssup.it> */ |
#include <linuxcomp.h> |
|
#include <sys/types.h> |
#include <string.h> |
#include <stdio.h> |
#include <unistd.h> |
#include <stdlib.h> |
#include <linux/pci.h> |
#include <linux/init.h> |
#include <linux/module.h> |
#include <linux/delay.h> |
#include <linux/errno.h> |
#include <linux/fs.h> |
#include <linux/kernel.h> |
#include <linux/sched.h> |
#include <linux/interrupt.h> |
#include <linux/kdev_t.h> |
|
#include "drivers/compbttv.h" |
#include "drivers/bttv.h" |
#include "drivers/tuner.h" |
#include <asm/io.h> |
#include <asm/byteorder.h> |
|
#define DEBUG(x) /* Debug driver */ |
#define MIN(a,b) (((a)>(b))?(b):(a)) |
#define MAX(a,b) (((a)>(b))?(a):(b)) |
#include "drivers/bttvp.h" |
|
static inline int copy_to_user (void *s, void *d, size_t size) |
{ |
memcpy(s,d,size); |
return 0; |
unsigned int bttv_num; /* number of Bt848s in use */ |
struct bttv bttvs[BTTV_MAX]; |
|
} |
unsigned int bttv_debug = 0; |
unsigned int bttv_verbose = 1; |
unsigned int bttv_gpio = 0; |
|
static inline int copy_from_user (void *s, void *d, size_t size) |
{ |
memcpy(s,d,size); |
return 0; |
|
} |
|
static void bt848_set_risc_jmps(struct bttv *btv, int state); |
|
int bttv_num; /* number of Bt848s in use */ |
int in_irq = 0; |
DWORD oldpci_command; |
struct bttv *btvirq; |
struct bttv bttvs[BTTV_MAX]; |
|
/* configuration variables */ |
#if defined(__sparc__) || defined(__powerpc__) || defined(__hppa__) |
/* config variables */ |
#ifdef __BIG_ENDIAN |
static unsigned int bigendian=1; |
#else |
static unsigned int bigendian=0; |
#endif |
static unsigned int radio[BTTV_MAX]; |
static unsigned int fieldnr = 0; |
static unsigned int irq_debug = 0; |
static unsigned int gbuffers = 2; |
unsigned int gbufsize = BTTV_MAX_FBUF; |
static int latency = -1; |
static unsigned int gbuffers = 8; |
static unsigned int gbufsize = 0x208000; |
|
static unsigned int combfilter = 0; |
static unsigned int lumafilter = 0; |
static unsigned int chroma_agc = 0; |
static unsigned int adc_crush = 1; |
unsigned int bttv_debug = 0; |
unsigned int bttv_verbose = 1; |
unsigned int bttv_gpio = 0; |
static int video_nr = -1; |
static int radio_nr = -1; |
static int vbi_nr = -1; |
|
//#define I2C_TIMING (0x7<<4) |
//#define I2C_DELAY 10 |
static unsigned int fdsr = 0; |
|
#define I2C_TIMING (0x3<<4) |
#define I2C_DELAY 40 |
/* options */ |
static unsigned int combfilter = 0; |
static unsigned int lumafilter = 0; |
static unsigned int automute = 1; |
static unsigned int chroma_agc = 0; |
static unsigned int adc_crush = 1; |
static unsigned int vcr_hack = 0; |
static unsigned int irq_iswitch = 0; |
|
#define I2C_SET(CTRL,DATA) \ |
{ btwrite((CTRL<<1)|(DATA), BT848_I2C); udelay(I2C_DELAY); } |
#define I2C_GET() (btread(BT848_I2C)&1) |
/* API features (turn on/off stuff for testing) */ |
static unsigned int sloppy = 0; |
static unsigned int v4l2 = 1; |
|
#define BURSTOFFSET 76 |
#define BTTV_ERRORS 5 |
|
/* insmod args */ |
MODULE_PARM(radio,"1-" __stringify(BTTV_MAX) "i"); |
MODULE_PARM_DESC(radio,"The TV card supports radio, default is 0 (no)"); |
MODULE_PARM(bigendian,"i"); |
MODULE_PARM_DESC(bigendian,"byte order of the framebuffer, default is native endian"); |
MODULE_PARM(bttv_verbose,"i"); |
MODULE_PARM_DESC(bttv_verbose,"verbose startup messages, default is 1 (yes)"); |
MODULE_PARM(bttv_gpio,"i"); |
MODULE_PARM_DESC(bttv_gpio,"log gpio changes, default is 0 (no)"); |
MODULE_PARM(bttv_debug,"i"); |
MODULE_PARM_DESC(bttv_debug,"debug messages, default is 0 (no)"); |
MODULE_PARM(irq_debug,"i"); |
MODULE_PARM_DESC(irq_debug,"irq handler debug messages, default is 0 (no)"); |
MODULE_PARM(gbuffers,"i"); |
MODULE_PARM_DESC(gbuffers,"number of capture buffers. range 2-32, default 8"); |
MODULE_PARM(gbufsize,"i"); |
MODULE_PARM_DESC(gbufsize,"size of the capture buffers, default is 0x208000"); |
|
/*******************************/ |
/* Memory management functions */ |
/*******************************/ |
MODULE_PARM(video_nr,"i"); |
MODULE_PARM(radio_nr,"i"); |
MODULE_PARM(vbi_nr,"i"); |
|
#define MDEBUG(x) do { } while(0) /* Debug memory management */ |
MODULE_PARM(fdsr,"i"); |
|
/* [DaveM] I've recoded most of this so that: |
* 1) It's easier to tell what is happening |
* 2) It's more portable, especially for translating things |
* out of vmalloc mapped areas in the kernel. |
* 3) Less unnecessary translations happen. |
* |
* The code used to assume that the kernel vmalloc mappings |
* existed in the page tables of every process, this is simply |
* not guarenteed. We now use pgd_offset_k which is the |
* defined way to get at the kernel page tables. |
*/ |
MODULE_PARM(combfilter,"i"); |
MODULE_PARM(lumafilter,"i"); |
MODULE_PARM(automute,"i"); |
MODULE_PARM_DESC(automute,"mute audio on bad/missing video signal, default is 1 (yes)"); |
MODULE_PARM(chroma_agc,"i"); |
MODULE_PARM_DESC(chroma_agc,"enables the AGC of chroma signal, default is 0 (no)"); |
MODULE_PARM(adc_crush,"i"); |
MODULE_PARM_DESC(adc_crush,"enables the luminance ADC crush, default is 1 (yes)"); |
MODULE_PARM(vcr_hack,"i"); |
MODULE_PARM_DESC(vcr_hack,"enables the VCR hack (improves synch on poor VCR tapes), default is 0 (no)"); |
MODULE_PARM(irq_iswitch,"i"); |
MODULE_PARM_DESC(irq_iswitch,"switch inputs in irq handler"); |
|
MODULE_PARM(sloppy,"i"); |
MODULE_PARM(v4l2,"i"); |
|
static inline unsigned long cpu_to_le32(unsigned long adr) |
{ |
return adr; |
} |
MODULE_DESCRIPTION("bttv - v4l/v4l2 driver module for bt848/878 based cards"); |
MODULE_AUTHOR("Ralph Metzler & Marcus Metzler & Gerd Knorr"); |
MODULE_LICENSE("GPL"); |
|
static inline unsigned long virt_to_bus(unsigned long adr) |
/* kernel args */ |
#ifndef MODULE |
static int __init p_radio(char *str) { return bttv_parse(str,BTTV_MAX,radio); } |
__setup("bttv.radio=", p_radio); |
#endif |
|
/* ----------------------------------------------------------------------- */ |
/* sysfs */ |
|
static ssize_t show_card(struct class_device *cd, char *buf) |
{ |
return (unsigned long)(adr); |
struct video_device *vfd = to_video_device(cd); |
struct bttv *btv = dev_get_drvdata(vfd->dev); |
return sprintf(buf, "%d\n", btv ? btv->type : UNSET); |
} |
static CLASS_DEVICE_ATTR(card, S_IRUGO, show_card, NULL); |
|
static void * vmalloc_32(size_t size) |
/* ----------------------------------------------------------------------- */ |
/* static data */ |
|
/* special timing tables from conexant... */ |
static u8 SRAM_Table[][60] = |
{ |
void *mem; |
unsigned long diff; |
/* PAL digital input over GPIO[7:0] */ |
{ |
45, // 45 bytes following |
0x36,0x11,0x01,0x00,0x90,0x02,0x05,0x10,0x04,0x16, |
0x12,0x05,0x11,0x00,0x04,0x12,0xC0,0x00,0x31,0x00, |
0x06,0x51,0x08,0x03,0x89,0x08,0x07,0xC0,0x44,0x00, |
0x81,0x01,0x01,0xA9,0x0D,0x02,0x02,0x50,0x03,0x37, |
0x37,0x00,0xAF,0x21,0x00 |
}, |
/* NTSC digital input over GPIO[7:0] */ |
{ |
51, // 51 bytes following |
0x0C,0xC0,0x00,0x00,0x90,0x02,0x03,0x10,0x03,0x06, |
0x10,0x04,0x12,0x12,0x05,0x02,0x13,0x04,0x19,0x00, |
0x04,0x39,0x00,0x06,0x59,0x08,0x03,0x83,0x08,0x07, |
0x03,0x50,0x00,0xC0,0x40,0x00,0x86,0x01,0x01,0xA6, |
0x0D,0x02,0x03,0x11,0x01,0x05,0x37,0x00,0xAC,0x21, |
0x00, |
}, |
// TGB_NTSC392 // quartzsight |
// This table has been modified to be used for Fusion Rev D |
{ |
0x2A, // size of table = 42 |
0x06, 0x08, 0x04, 0x0a, 0xc0, 0x00, 0x18, 0x08, 0x03, 0x24, |
0x08, 0x07, 0x02, 0x90, 0x02, 0x08, 0x10, 0x04, 0x0c, 0x10, |
0x05, 0x2c, 0x11, 0x04, 0x55, 0x48, 0x00, 0x05, 0x50, 0x00, |
0xbf, 0x0c, 0x02, 0x2f, 0x3d, 0x00, 0x2f, 0x3f, 0x00, 0xc3, |
0x20, 0x00 |
} |
}; |
|
mem = malloc(size+8); |
const struct bttv_tvnorm bttv_tvnorms[] = { |
/* PAL-BDGHI */ |
/* max. active video is actually 922, but 924 is divisible by 4 and 3! */ |
/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ |
{ |
.v4l2_id = V4L2_STD_PAL, |
.name = "PAL", |
.Fsc = 35468950, |
.swidth = 924, |
.sheight = 576, |
.totalwidth = 1135, |
.adelay = 0x7f, |
.bdelay = 0x72, |
.iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), |
.scaledtwidth = 1135, |
.hdelayx1 = 186, |
.hactivex1 = 924, |
.vdelay = 0x20, |
.vbipack = 255, |
.sram = 0, |
},{ |
.v4l2_id = V4L2_STD_NTSC_M, |
.name = "NTSC", |
.Fsc = 28636363, |
.swidth = 768, |
.sheight = 480, |
.totalwidth = 910, |
.adelay = 0x68, |
.bdelay = 0x5d, |
.iform = (BT848_IFORM_NTSC|BT848_IFORM_XT0), |
.scaledtwidth = 910, |
.hdelayx1 = 128, |
.hactivex1 = 910, |
.vdelay = 0x1a, |
.vbipack = 144, |
.sram = 1, |
},{ |
.v4l2_id = V4L2_STD_SECAM, |
.name = "SECAM", |
.Fsc = 35468950, |
.swidth = 924, |
.sheight = 576, |
.totalwidth = 1135, |
.adelay = 0x7f, |
.bdelay = 0xb0, |
.iform = (BT848_IFORM_SECAM|BT848_IFORM_XT1), |
.scaledtwidth = 1135, |
.hdelayx1 = 186, |
.hactivex1 = 922, |
.vdelay = 0x20, |
.vbipack = 255, |
.sram = 0, /* like PAL, correct? */ |
},{ |
.v4l2_id = V4L2_STD_PAL_Nc, |
.name = "PAL-Nc", |
.Fsc = 28636363, |
.swidth = 640, |
.sheight = 576, |
.totalwidth = 910, |
.adelay = 0x68, |
.bdelay = 0x5d, |
.iform = (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), |
.scaledtwidth = 780, |
.hdelayx1 = 130, |
.hactivex1 = 734, |
.vdelay = 0x1a, |
.vbipack = 144, |
.sram = -1, |
},{ |
.v4l2_id = V4L2_STD_PAL_M, |
.name = "PAL-M", |
.Fsc = 28636363, |
.swidth = 640, |
.sheight = 480, |
.totalwidth = 910, |
.adelay = 0x68, |
.bdelay = 0x5d, |
.iform = (BT848_IFORM_PAL_M|BT848_IFORM_XT0), |
.scaledtwidth = 780, |
.hdelayx1 = 135, |
.hactivex1 = 754, |
.vdelay = 0x1a, |
.vbipack = 144, |
.sram = -1, |
},{ |
.v4l2_id = V4L2_STD_PAL_N, |
.name = "PAL-N", |
.Fsc = 35468950, |
.swidth = 768, |
.sheight = 576, |
.totalwidth = 1135, |
.adelay = 0x7f, |
.bdelay = 0x72, |
.iform = (BT848_IFORM_PAL_N|BT848_IFORM_XT1), |
.scaledtwidth = 944, |
.hdelayx1 = 186, |
.hactivex1 = 922, |
.vdelay = 0x20, |
.vbipack = 144, |
.sram = -1, |
},{ |
.v4l2_id = V4L2_STD_NTSC_M_JP, |
.name = "NTSC-JP", |
.Fsc = 28636363, |
.swidth = 640, |
.sheight = 480, |
.totalwidth = 910, |
.adelay = 0x68, |
.bdelay = 0x5d, |
.iform = (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), |
.scaledtwidth = 780, |
.hdelayx1 = 135, |
.hactivex1 = 754, |
.vdelay = 0x16, |
.vbipack = 144, |
.sram = -1, |
},{ |
/* that one hopefully works with the strange timing |
* which video recorders produce when playing a NTSC |
* tape on a PAL TV ... */ |
.v4l2_id = V4L2_STD_PAL_60, |
.name = "PAL-60", |
.Fsc = 35468950, |
.swidth = 924, |
.sheight = 480, |
.totalwidth = 1135, |
.adelay = 0x7f, |
.bdelay = 0x72, |
.iform = (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), |
.scaledtwidth = 1135, |
.hdelayx1 = 186, |
.hactivex1 = 924, |
.vdelay = 0x1a, |
.vbipack = 255, |
.vtotal = 524, |
.sram = -1, |
} |
}; |
const unsigned int BTTV_TVNORMS = ARRAY_SIZE(bttv_tvnorms); |
|
diff = (unsigned long)((((unsigned long)mem/4)+1)*4-(unsigned long)mem); |
|
*(unsigned long *)(mem+diff) = (unsigned long)diff; |
|
return (mem+diff+4); |
|
} |
/* ----------------------------------------------------------------------- */ |
/* bttv format list |
packed pixel formats must come first */ |
const struct bttv_format bttv_formats[] = { |
{ |
.name = "8 bpp, gray", |
.palette = VIDEO_PALETTE_GREY, |
.fourcc = V4L2_PIX_FMT_GREY, |
.btformat = BT848_COLOR_FMT_Y8, |
.depth = 8, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "8 bpp, dithered color", |
.palette = VIDEO_PALETTE_HI240, |
.fourcc = V4L2_PIX_FMT_HI240, |
.btformat = BT848_COLOR_FMT_RGB8, |
.depth = 8, |
.flags = FORMAT_FLAGS_PACKED | FORMAT_FLAGS_DITHER, |
},{ |
.name = "15 bpp RGB, le", |
.palette = VIDEO_PALETTE_RGB555, |
.fourcc = V4L2_PIX_FMT_RGB555, |
.btformat = BT848_COLOR_FMT_RGB15, |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "15 bpp RGB, be", |
.palette = -1, |
.fourcc = V4L2_PIX_FMT_RGB555X, |
.btformat = BT848_COLOR_FMT_RGB15, |
.btswap = 0x03, /* byteswap */ |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "16 bpp RGB, le", |
.palette = VIDEO_PALETTE_RGB565, |
.fourcc = V4L2_PIX_FMT_RGB565, |
.btformat = BT848_COLOR_FMT_RGB16, |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "16 bpp RGB, be", |
.palette = -1, |
.fourcc = V4L2_PIX_FMT_RGB565X, |
.btformat = BT848_COLOR_FMT_RGB16, |
.btswap = 0x03, /* byteswap */ |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "24 bpp RGB, le", |
.palette = VIDEO_PALETTE_RGB24, |
.fourcc = V4L2_PIX_FMT_BGR24, |
.btformat = BT848_COLOR_FMT_RGB24, |
.depth = 24, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "32 bpp RGB, le", |
.palette = VIDEO_PALETTE_RGB32, |
.fourcc = V4L2_PIX_FMT_BGR32, |
.btformat = BT848_COLOR_FMT_RGB32, |
.depth = 32, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "32 bpp RGB, be", |
.palette = -1, |
.fourcc = V4L2_PIX_FMT_RGB32, |
.btformat = BT848_COLOR_FMT_RGB32, |
.btswap = 0x0f, /* byte+word swap */ |
.depth = 32, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "4:2:2, packed, YUYV", |
.palette = VIDEO_PALETTE_YUV422, |
.fourcc = V4L2_PIX_FMT_YUYV, |
.btformat = BT848_COLOR_FMT_YUY2, |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "4:2:2, packed, YUYV", |
.palette = VIDEO_PALETTE_YUYV, |
.fourcc = V4L2_PIX_FMT_YUYV, |
.btformat = BT848_COLOR_FMT_YUY2, |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "4:2:2, packed, UYVY", |
.palette = VIDEO_PALETTE_UYVY, |
.fourcc = V4L2_PIX_FMT_UYVY, |
.btformat = BT848_COLOR_FMT_YUY2, |
.btswap = 0x03, /* byteswap */ |
.depth = 16, |
.flags = FORMAT_FLAGS_PACKED, |
},{ |
.name = "4:2:2, planar, Y-Cb-Cr", |
.palette = VIDEO_PALETTE_YUV422P, |
.fourcc = V4L2_PIX_FMT_YUV422P, |
.btformat = BT848_COLOR_FMT_YCrCb422, |
.depth = 16, |
.flags = FORMAT_FLAGS_PLANAR, |
.hshift = 1, |
.vshift = 0, |
},{ |
.name = "4:2:0, planar, Y-Cb-Cr", |
.palette = VIDEO_PALETTE_YUV420P, |
.fourcc = V4L2_PIX_FMT_YUV420, |
.btformat = BT848_COLOR_FMT_YCrCb422, |
.depth = 12, |
.flags = FORMAT_FLAGS_PLANAR, |
.hshift = 1, |
.vshift = 1, |
},{ |
.name = "4:2:0, planar, Y-Cr-Cb", |
.palette = -1, |
.fourcc = V4L2_PIX_FMT_YVU420, |
.btformat = BT848_COLOR_FMT_YCrCb422, |
.depth = 12, |
.flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb, |
.hshift = 1, |
.vshift = 1, |
},{ |
.name = "4:1:1, planar, Y-Cb-Cr", |
.palette = VIDEO_PALETTE_YUV411P, |
.fourcc = V4L2_PIX_FMT_YUV411P, |
.btformat = BT848_COLOR_FMT_YCrCb411, |
.depth = 12, |
.flags = FORMAT_FLAGS_PLANAR, |
.hshift = 2, |
.vshift = 0, |
},{ |
.name = "4:1:0, planar, Y-Cb-Cr", |
.palette = VIDEO_PALETTE_YUV410P, |
.fourcc = V4L2_PIX_FMT_YUV410, |
.btformat = BT848_COLOR_FMT_YCrCb411, |
.depth = 9, |
.flags = FORMAT_FLAGS_PLANAR, |
.hshift = 2, |
.vshift = 2, |
},{ |
.name = "4:1:0, planar, Y-Cr-Cb", |
.palette = -1, |
.fourcc = V4L2_PIX_FMT_YVU410, |
.btformat = BT848_COLOR_FMT_YCrCb411, |
.depth = 9, |
.flags = FORMAT_FLAGS_PLANAR | FORMAT_FLAGS_CrCb, |
.hshift = 2, |
.vshift = 2, |
},{ |
.name = "raw scanlines", |
.palette = VIDEO_PALETTE_RAW, |
.fourcc = -1, |
.btformat = BT848_COLOR_FMT_RAW, |
.depth = 8, |
.flags = FORMAT_FLAGS_RAW, |
} |
}; |
const unsigned int BTTV_FORMATS = ARRAY_SIZE(bttv_formats); |
|
static void vfree_32(void *ptr) |
/* ----------------------------------------------------------------------- */ |
|
#define V4L2_CID_PRIVATE_CHROMA_AGC (V4L2_CID_PRIVATE_BASE + 0) |
#define V4L2_CID_PRIVATE_COMBFILTER (V4L2_CID_PRIVATE_BASE + 1) |
#define V4L2_CID_PRIVATE_AUTOMUTE (V4L2_CID_PRIVATE_BASE + 2) |
#define V4L2_CID_PRIVATE_LUMAFILTER (V4L2_CID_PRIVATE_BASE + 3) |
#define V4L2_CID_PRIVATE_AGC_CRUSH (V4L2_CID_PRIVATE_BASE + 4) |
#define V4L2_CID_PRIVATE_VCR_HACK (V4L2_CID_PRIVATE_BASE + 5) |
#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 6) |
|
static const struct v4l2_queryctrl no_ctl = { |
.name = "42", |
.flags = V4L2_CTRL_FLAG_DISABLED, |
}; |
static const struct v4l2_queryctrl bttv_ctls[] = { |
/* --- video --- */ |
{ |
.id = V4L2_CID_BRIGHTNESS, |
.name = "Brightness", |
.minimum = 0, |
.maximum = 65535, |
.step = 256, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
},{ |
.id = V4L2_CID_CONTRAST, |
.name = "Contrast", |
.minimum = 0, |
.maximum = 65535, |
.step = 128, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
},{ |
.id = V4L2_CID_SATURATION, |
.name = "Saturation", |
.minimum = 0, |
.maximum = 65535, |
.step = 128, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
},{ |
.id = V4L2_CID_HUE, |
.name = "Hue", |
.minimum = 0, |
.maximum = 65535, |
.step = 256, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
}, |
/* --- audio --- */ |
{ |
.id = V4L2_CID_AUDIO_MUTE, |
.name = "Mute", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
},{ |
.id = V4L2_CID_AUDIO_VOLUME, |
.name = "Volume", |
.minimum = 0, |
.maximum = 65535, |
.step = 65535/100, |
.default_value = 65535, |
.type = V4L2_CTRL_TYPE_INTEGER, |
},{ |
.id = V4L2_CID_AUDIO_BALANCE, |
.name = "Balance", |
.minimum = 0, |
.maximum = 65535, |
.step = 65535/100, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
},{ |
.id = V4L2_CID_AUDIO_BASS, |
.name = "Bass", |
.minimum = 0, |
.maximum = 65535, |
.step = 65535/100, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
},{ |
.id = V4L2_CID_AUDIO_TREBLE, |
.name = "Treble", |
.minimum = 0, |
.maximum = 65535, |
.step = 65535/100, |
.default_value = 32768, |
.type = V4L2_CTRL_TYPE_INTEGER, |
}, |
/* --- private --- */ |
{ |
.id = V4L2_CID_PRIVATE_CHROMA_AGC, |
.name = "chroma agc", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
},{ |
.id = V4L2_CID_PRIVATE_COMBFILTER, |
.name = "combfilter", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
},{ |
.id = V4L2_CID_PRIVATE_AUTOMUTE, |
.name = "automute", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
},{ |
.id = V4L2_CID_PRIVATE_LUMAFILTER, |
.name = "luma decimation filter", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
},{ |
.id = V4L2_CID_PRIVATE_AGC_CRUSH, |
.name = "agc crush", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
},{ |
.id = V4L2_CID_PRIVATE_VCR_HACK, |
.name = "vcr hack", |
.minimum = 0, |
.maximum = 1, |
.type = V4L2_CTRL_TYPE_BOOLEAN, |
} |
}; |
const int BTTV_CTLS = ARRAY_SIZE(bttv_ctls); |
|
/* ----------------------------------------------------------------------- */ |
/* resource management */ |
|
static |
int check_alloc_btres(struct bttv *btv, struct bttv_fh *fh, int bit) |
{ |
if (fh->resources & bit) |
/* have it already allocated */ |
return 1; |
|
free(ptr-4-*(unsigned long *)(ptr-4)); |
/* is it free? */ |
down(&btv->reslock); |
if (btv->resources & bit) { |
/* no, someone else uses it */ |
up(&btv->reslock); |
return 0; |
} |
/* it's free, grab it */ |
fh->resources |= bit; |
btv->resources |= bit; |
up(&btv->reslock); |
return 1; |
} |
|
static |
int check_btres(struct bttv_fh *fh, int bit) |
{ |
return (fh->resources & bit); |
} |
|
void bttv_gpio_tracking(struct bttv *btv, char *comment) |
static |
int locked_btres(struct bttv *btv, int bit) |
{ |
unsigned int outbits, data; |
outbits = btread(BT848_GPIO_OUT_EN); |
data = gpioread(); |
printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n", |
btv->nr,outbits,data & outbits, data & ~outbits, comment); |
return (btv->resources & bit); |
} |
|
static inline void bt848_dma(struct bttv *btv, uint state) |
static |
void free_btres(struct bttv *btv, struct bttv_fh *fh, int bits) |
{ |
if (state) |
btor(3, BT848_GPIO_DMA_CTL); |
else |
btand(~3, BT848_GPIO_DMA_CTL); |
#if 1 /* DEBUG */ |
if ((fh->resources & bits) != bits) { |
/* trying to free ressources not allocated by us ... */ |
printk("bttv: BUG! (btres)\n"); |
} |
#endif |
down(&btv->reslock); |
fh->resources &= ~bits; |
btv->resources &= ~bits; |
up(&btv->reslock); |
} |
|
/* ----------------------------------------------------------------------- */ |
/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC */ |
|
/* If Bt848a or Bt849, use PLL for PAL/SECAM and crystal for NTSC*/ |
|
/* Frequency = (F_input / PLL_X) * PLL_I.PLL_F/PLL_C |
PLL_X = Reference pre-divider (0=1, 1=2) |
PLL_C = Post divider (0=6, 1=4) |
192,1704 → 684,2685 |
fout=(fout%fin)*256; |
fl=fout/fin; |
|
/*printk("0x%02x 0x%02x 0x%02x\n", fi, fh, fl);*/ |
btwrite(fl, BT848_PLL_F_LO); |
btwrite(fh, BT848_PLL_F_HI); |
btwrite(fi|BT848_PLL_X, BT848_PLL_XCI); |
} |
|
static int set_pll(struct bttv *btv) |
static void set_pll(struct bttv *btv) |
{ |
int i; |
|
if (!btv->pll.pll_crystal) |
return 0; |
return; |
|
if (btv->pll.pll_ofreq == btv->pll.pll_current) { |
dprintk("bttv%d: PLL: no change required\n",btv->nr); |
return; |
} |
|
if (btv->pll.pll_ifreq == btv->pll.pll_ofreq) { |
/* no PLL needed */ |
if (btv->pll.pll_current == 0) { |
/* printk ("bttv%d: PLL: is off\n",btv->nr); */ |
return 0; |
} |
if (bttv_verbose) |
printk(KERN_INFO "bttv%d: PLL: switching off\n",btv->nr); |
if (btv->pll.pll_current == 0) |
return; |
vprintk(KERN_INFO "bttv%d: PLL can sleep, using XTAL (%d).\n", |
btv->nr,btv->pll.pll_ifreq); |
btwrite(0x00,BT848_TGCTRL); |
btwrite(0x00,BT848_PLL_XCI); |
btv->pll.pll_current = 0; |
return 0; |
return; |
} |
|
if (btv->pll.pll_ofreq == btv->pll.pll_current) { |
/* printk("bttv%d: PLL: no change required\n",btv->nr); */ |
return 1; |
} |
|
if (bttv_verbose) |
cprintf("[info ] bttv%d: PLL: %d => %d ... ",btv->nr, |
btv->pll.pll_ifreq, btv->pll.pll_ofreq); |
|
vprintk(KERN_INFO "bttv%d: PLL: %d => %d ",btv->nr, |
btv->pll.pll_ifreq, btv->pll.pll_ofreq); |
set_pll_freq(btv, btv->pll.pll_ifreq, btv->pll.pll_ofreq); |
|
/* Let other people run while the PLL stabilizes */ |
udelay(100000); |
|
for (i=0; i<100; i++) |
{ |
if ((btread(BT848_DSTATUS)&BT848_DSTATUS_PLOCK)) |
btwrite(0,BT848_DSTATUS); |
else |
{ |
for (i=0; i<10; i++) { |
/* Let other people run while the PLL stabilizes */ |
vprintk("."); |
current->state = TASK_INTERRUPTIBLE; |
schedule_timeout(HZ/10); |
|
if (btread(BT848_DSTATUS) & BT848_DSTATUS_PLOCK) { |
btwrite(0,BT848_DSTATUS); |
} else { |
btwrite(0x08,BT848_TGCTRL); |
btv->pll.pll_current = btv->pll.pll_ofreq; |
if (bttv_verbose) |
cprintf("ok\n"); |
return 1; |
vprintk(" ok\n"); |
return; |
} |
udelay(10000); |
} |
btv->pll.pll_current = 0; |
if (bttv_verbose) |
cprintf("oops\n"); |
return -1; |
btv->pll.pll_current = -1; |
vprintk("failed\n"); |
return; |
} |
|
static void bt848_muxsel(struct bttv *btv, unsigned int input) |
/* used to switch between the bt848's analog/digital video capture modes */ |
void bt848A_set_timing(struct bttv *btv) |
{ |
int i, len; |
int table_idx = bttv_tvnorms[btv->tvnorm].sram; |
int fsc = bttv_tvnorms[btv->tvnorm].Fsc; |
|
if (UNSET == bttv_tvcards[btv->type].muxsel[btv->input]) { |
dprintk("bttv%d: load digital timing table (table_idx=%d)\n", |
btv->nr,table_idx); |
|
/* timing change...reset timing generator address */ |
btwrite(0x00, BT848_TGCTRL); |
btwrite(0x02, BT848_TGCTRL); |
btwrite(0x00, BT848_TGCTRL); |
|
len=SRAM_Table[table_idx][0]; |
for(i = 1; i <= len; i++) |
btwrite(SRAM_Table[table_idx][i],BT848_TGLB); |
btv->pll.pll_ofreq = 27000000; |
|
set_pll(btv); |
btwrite(0x11, BT848_TGCTRL); |
btwrite(0x41, BT848_DVSIF); |
} else { |
btv->pll.pll_ofreq = fsc; |
set_pll(btv); |
btwrite(0x0, BT848_DVSIF); |
} |
} |
|
/* ----------------------------------------------------------------------- */ |
|
static void bt848_bright(struct bttv *btv, int bright) |
{ |
int value; |
|
btv->bright = bright; |
|
/* We want -128 to 127 we get 0-65535 */ |
value = (bright >> 8) - 128; |
btwrite(value & 0xff, BT848_BRIGHT); |
} |
|
static void bt848_hue(struct bttv *btv, int hue) |
{ |
int value; |
|
btv->hue = hue; |
|
/* -128 to 127 */ |
value = (hue >> 8) - 128; |
btwrite(value & 0xff, BT848_HUE); |
} |
|
static void bt848_contrast(struct bttv *btv, int cont) |
{ |
int value,hibit; |
|
btv->contrast = cont; |
|
/* 0-511 */ |
value = (cont >> 7); |
hibit = (value >> 6) & 4; |
btwrite(value & 0xff, BT848_CONTRAST_LO); |
btaor(hibit, ~4, BT848_E_CONTROL); |
btaor(hibit, ~4, BT848_O_CONTROL); |
} |
|
static void bt848_sat(struct bttv *btv, int color) |
{ |
int val_u,val_v,hibits; |
|
btv->saturation = color; |
|
/* 0-511 for the color */ |
val_u = color >> 7; |
val_v = ((color>>7)*180L)/254; |
hibits = (val_u >> 7) & 2; |
hibits |= (val_v >> 8) & 1; |
btwrite(val_u & 0xff, BT848_SAT_U_LO); |
btwrite(val_v & 0xff, BT848_SAT_V_LO); |
btaor(hibits, ~3, BT848_E_CONTROL); |
btaor(hibits, ~3, BT848_O_CONTROL); |
} |
|
/* ----------------------------------------------------------------------- */ |
|
static int |
video_mux(struct bttv *btv, unsigned int input) |
{ |
int mux,mask2; |
|
if (input >= bttv_tvcards[btv->type].video_inputs) |
return -EINVAL; |
|
/* needed by RemoteVideo MX */ |
btor(bttv_tvcards[btv->type].gpiomask2,BT848_GPIO_OUT_EN); |
mask2 = bttv_tvcards[btv->type].gpiomask2; |
if (mask2) |
btaor(mask2,~mask2,BT848_GPIO_OUT_EN); |
|
/* This seems to get rid of some synchronization problems */ |
btand(~(3<<5), BT848_IFORM); |
udelay(10000); |
|
input %= bttv_tvcards[btv->type].video_inputs; |
if (input==bttv_tvcards[btv->type].svhs) |
{ |
if (input == btv->svhs) { |
btor(BT848_CONTROL_COMP, BT848_E_CONTROL); |
btor(BT848_CONTROL_COMP, BT848_O_CONTROL); |
} |
else |
{ |
} else { |
btand(~BT848_CONTROL_COMP, BT848_E_CONTROL); |
btand(~BT848_CONTROL_COMP, BT848_O_CONTROL); |
} |
mux = bttv_tvcards[btv->type].muxsel[input] & 3; |
btaor(mux<<5, ~(3<<5), BT848_IFORM); |
dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n", |
btv->nr,input,mux); |
|
btaor((bttv_tvcards[btv->type].muxsel[input]&3)<<5, ~(3<<5), BT848_IFORM); |
/* card specific hook */ |
if(bttv_tvcards[btv->type].muxsel_hook) |
bttv_tvcards[btv->type].muxsel_hook (btv, input); |
return 0; |
} |
|
gpioaor(bttv_tvcards[btv->type].muxsel[input]>>4, |
~bttv_tvcards[btv->type].gpiomask2); |
static char *audio_modes[] = { |
"audio: tuner", "audio: radio", "audio: extern", |
"audio: intern", "audio: off" |
}; |
|
/* card specific hook */ |
if( bttv_tvcards[btv->type].muxsel_hook ) |
bttv_tvcards[btv->type].muxsel_hook ( btv, input ); |
static int |
audio_mux(struct bttv *btv, int mode) |
{ |
int val,mux,i2c_mux,signal; |
|
btaor(bttv_tvcards[btv->type].gpiomask, |
~bttv_tvcards[btv->type].gpiomask,BT848_GPIO_OUT_EN); |
signal = btread(BT848_DSTATUS) & BT848_DSTATUS_HLOC; |
|
switch (mode) { |
case AUDIO_MUTE: |
btv->audio |= AUDIO_MUTE; |
break; |
case AUDIO_UNMUTE: |
btv->audio &= ~AUDIO_MUTE; |
break; |
case AUDIO_TUNER: |
case AUDIO_RADIO: |
case AUDIO_EXTERN: |
case AUDIO_INTERN: |
btv->audio &= AUDIO_MUTE; |
btv->audio |= mode; |
} |
i2c_mux = mux = (btv->audio & AUDIO_MUTE) ? AUDIO_OFF : btv->audio; |
if (btv->opt_automute && !signal && !btv->radio_user) |
mux = AUDIO_OFF; |
#if 0 |
printk("bttv%d: amux: mode=%d audio=%d signal=%s mux=%d/%d irq=%s\n", |
btv->nr, mode, btv->audio, signal ? "yes" : "no", |
mux, i2c_mux, in_interrupt() ? "yes" : "no"); |
#endif |
|
val = bttv_tvcards[btv->type].audiomux[mux]; |
btaor(val,~bttv_tvcards[btv->type].gpiomask, BT848_GPIO_DATA); |
if (bttv_gpio) |
bttv_gpio_tracking(btv,"muxsel"); |
bttv_gpio_tracking(btv,audio_modes[mux]); |
if (!in_interrupt()) |
bttv_call_i2c_clients(btv,AUDC_SET_INPUT,&(i2c_mux)); |
return 0; |
} |
|
static void |
i2c_vidiocschan(struct bttv *btv) |
{ |
struct video_channel c; |
|
memset(&c,0,sizeof(c)); |
c.norm = btv->tvnorm; |
c.channel = btv->input; |
bttv_call_i2c_clients(btv,VIDIOCSCHAN,&c); |
if (btv->type == BTTV_VOODOOTV_FM) |
bttv_tda9880_setnorm(btv,c.norm); |
} |
|
|
struct tvnorm |
static int |
set_tvnorm(struct bttv *btv, unsigned int norm) |
{ |
u32 Fsc; |
u16 swidth, sheight; /* scaled standard width, height */ |
u16 totalwidth; |
u8 adelay, bdelay, iform; |
u32 scaledtwidth; |
u16 hdelayx1, hactivex1; |
u16 vdelay; |
u8 vbipack; |
}; |
const struct bttv_tvnorm *tvnorm; |
|
static struct tvnorm tvnorms[] = { |
/* PAL-BDGHI */ |
/* max. active video is actually 922, but 924 is divisible by 4 and 3! */ |
/* actually, max active PAL with HSCALE=0 is 948, NTSC is 768 - nil */ |
{ 35468950, |
924, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_BDGHI|BT848_IFORM_XT1), |
1135, 186, 924, |
#ifdef VIDEODAT_HACK |
VBI_MAXLINES*2, |
#else |
0x20, |
#endif |
255}, |
if (norm < 0 || norm >= BTTV_TVNORMS) |
return -EINVAL; |
|
/* NTSC */ |
{ 28636363, |
768, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC|BT848_IFORM_XT0), |
910, 128, 910, 0x1a, 144}, |
btv->tvnorm = norm; |
tvnorm = &bttv_tvnorms[norm]; |
|
btwrite(tvnorm->adelay, BT848_ADELAY); |
btwrite(tvnorm->bdelay, BT848_BDELAY); |
btaor(tvnorm->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), |
BT848_IFORM); |
btwrite(tvnorm->vbipack, BT848_VBI_PACK_SIZE); |
btwrite(1, BT848_VBI_PACK_DEL); |
bt848A_set_timing(btv); |
|
switch (btv->type) { |
case BTTV_VOODOOTV_FM: |
bttv_tda9880_setnorm(btv,norm); |
break; |
#if 0 |
/* SECAM EAST */ |
{ 35468950, |
768, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), |
944, 186, 922, 0x20, 255}, |
#else |
/* SECAM L */ |
{ 35468950, |
924, 576, 1135, 0x7f, 0xb0, (BT848_IFORM_SECAM|BT848_IFORM_XT1), |
1135, 186, 922, 0x20, 255}, |
case BTTV_OSPREY540: |
osprey_540_set_norm(btv,norm); |
break; |
#endif |
/* PAL-NC */ |
{ 28636363, |
640, 576, 910, 0x68, 0x5d, (BT848_IFORM_PAL_NC|BT848_IFORM_XT0), |
780, 130, 734, 0x1a, 144}, |
/* PAL-M */ |
{ 28636363, |
640, 480, 910, 0x68, 0x5d, (BT848_IFORM_PAL_M|BT848_IFORM_XT0), |
780, 135, 754, 0x1a, 144}, |
/* PAL-N */ |
{ 35468950, |
768, 576, 1135, 0x7f, 0x72, (BT848_IFORM_PAL_N|BT848_IFORM_XT1), |
944, 186, 922, 0x20, 144}, |
/* NTSC-Japan */ |
{ 28636363, |
640, 480, 910, 0x68, 0x5d, (BT848_IFORM_NTSC_J|BT848_IFORM_XT0), |
780, 135, 754, 0x16, 144}, |
}; |
#define TVNORMS (sizeof(tvnorms)/sizeof(tvnorm)) |
#define VBI_SPL 2044 |
} |
return 0; |
} |
|
/* RISC command to write one VBI data line */ |
#define VBI_RISC BT848_RISC_WRITE|VBI_SPL|BT848_RISC_EOL|BT848_RISC_SOL |
|
static void make_vbitab(struct bttv *btv) |
static void |
set_input(struct bttv *btv, unsigned int input) |
{ |
int i; |
unsigned int *po=(unsigned int *) btv->vbi_odd; |
unsigned int *pe=(unsigned int *) btv->vbi_even; |
|
if (bttv_debug > 1) |
cprintf("bttv%d: vbi1: po=%08lx pe=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)po), virt_to_bus((unsigned long)pe)); |
|
*(po++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(po++)=0; |
for (i=0; i<VBI_MAXLINES; i++) |
{ |
*(po++)=cpu_to_le32(VBI_RISC); |
*(po++)=cpu_to_le32(virt_to_bus((unsigned long)btv->vbibuf+i*2048)); |
unsigned long flags; |
|
btv->input = input; |
if (irq_iswitch) { |
spin_lock_irqsave(&btv->s_lock,flags); |
if (btv->curr.irqflags) { |
/* active capture -> delayed input switch */ |
btv->new_input = input; |
} else { |
video_mux(btv,input); |
} |
spin_unlock_irqrestore(&btv->s_lock,flags); |
} else { |
video_mux(btv,input); |
} |
*(po++)=cpu_to_le32(BT848_RISC_JUMP); |
*(po++)=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+16)); |
audio_mux(btv,(input == bttv_tvcards[btv->type].tuner ? |
AUDIO_TUNER : AUDIO_EXTERN)); |
set_tvnorm(btv,btv->tvnorm); |
} |
|
*(pe++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); *(pe++)=0; |
for (i=VBI_MAXLINES; i<VBI_MAXLINES*2; i++) |
{ |
*(pe++)=cpu_to_le32(VBI_RISC); |
*(pe++)=cpu_to_le32(virt_to_bus((unsigned long)btv->vbibuf+i*2048)); |
static void init_bt848(struct bttv *btv) |
{ |
int val; |
|
btwrite(0, BT848_SRESET); |
btwrite(0x00, BT848_CAP_CTL); |
btwrite(BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); |
btwrite(BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, BT848_IFORM); |
|
/* set planar and packed mode trigger points and */ |
/* set rising edge of inverted GPINTR pin as irq trigger */ |
btwrite(BT848_GPIO_DMA_CTL_PKTP_32| |
BT848_GPIO_DMA_CTL_PLTP1_16| |
BT848_GPIO_DMA_CTL_PLTP23_16| |
BT848_GPIO_DMA_CTL_GPINTC| |
BT848_GPIO_DMA_CTL_GPINTI, |
BT848_GPIO_DMA_CTL); |
|
val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; |
btwrite(val, BT848_E_SCLOOP); |
btwrite(val, BT848_O_SCLOOP); |
|
btwrite(0x20, BT848_E_VSCALE_HI); |
btwrite(0x20, BT848_O_VSCALE_HI); |
btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), |
BT848_ADC); |
|
if (btv->opt_lumafilter) { |
btwrite(0, BT848_E_CONTROL); |
btwrite(0, BT848_O_CONTROL); |
} else { |
btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); |
btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); |
} |
*(pe++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(0x01<<16)); |
*(pe++)=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+40)); |
|
if (bttv_debug > 1) |
cprintf("bttv%d: vbi2: po=%08lx pe=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)po), virt_to_bus((unsigned long)pe)); |
/* interrupt */ |
btwrite(0xfffffUL, BT848_INT_STAT); |
btwrite((btv->triton1) | |
BT848_INT_GPINT | |
BT848_INT_SCERR | |
(fdsr ? BT848_INT_FDSR : 0) | |
BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| |
BT848_INT_FMTCHG|BT848_INT_HLOCK, |
BT848_INT_MASK); |
} |
|
static int fmtbppx2[16] = { |
8, 6, 4, 4, 4, 3, 2, 2, 4, 3, 0, 0, 0, 0, 2, 0 |
}; |
extern void bttv_reinit_bt848(struct bttv *btv) |
{ |
unsigned long flags; |
|
static int palette2fmt[] = { |
0, |
BT848_COLOR_FMT_Y8, |
BT848_COLOR_FMT_RGB8, |
BT848_COLOR_FMT_RGB16, |
BT848_COLOR_FMT_RGB24, |
BT848_COLOR_FMT_RGB32, |
BT848_COLOR_FMT_RGB15, |
BT848_COLOR_FMT_YUY2, |
BT848_COLOR_FMT_YUY2, |
-1, |
-1, |
-1, |
BT848_COLOR_FMT_RAW, |
BT848_COLOR_FMT_YCrCb422, |
BT848_COLOR_FMT_YCrCb411, |
BT848_COLOR_FMT_YCrCb422, |
BT848_COLOR_FMT_YCrCb411, |
}; |
#define PALETTEFMT_MAX (sizeof(palette2fmt)/sizeof(int)) |
if (bttv_verbose) |
printk(KERN_INFO "bttv%d: reset, reinitialize\n",btv->nr); |
spin_lock_irqsave(&btv->s_lock,flags); |
btv->errors=0; |
bttv_set_dma(btv,0,0); |
spin_unlock_irqrestore(&btv->s_lock,flags); |
|
static int make_rawrisctab(struct bttv *btv, unsigned int *ro, |
unsigned int *re, unsigned int *vbuf) |
init_bt848(btv); |
btv->pll.pll_current = -1; |
set_input(btv,btv->input); |
} |
|
static int get_control(struct bttv *btv, struct v4l2_control *c) |
{ |
unsigned long line; |
unsigned long bpl=1024; /* bytes per line */ |
unsigned long vadr = (unsigned long)vbuf; |
struct video_audio va; |
int i; |
|
for (i = 0; i < BTTV_CTLS; i++) |
if (bttv_ctls[i].id == c->id) |
break; |
if (i == BTTV_CTLS) |
return -EINVAL; |
if (i >= 4 && i <= 8) { |
memset(&va,0,sizeof(va)); |
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); |
if (btv->audio_hook) |
btv->audio_hook(btv,&va,0); |
} |
switch (c->id) { |
case V4L2_CID_BRIGHTNESS: |
c->value = btv->bright; |
break; |
case V4L2_CID_HUE: |
c->value = btv->hue; |
break; |
case V4L2_CID_CONTRAST: |
c->value = btv->contrast; |
break; |
case V4L2_CID_SATURATION: |
c->value = btv->saturation; |
break; |
|
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); |
*(ro++)=cpu_to_le32(0); |
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); |
*(re++)=cpu_to_le32(0); |
|
/* In PAL 650 blocks of 256 DWORDs are sampled, but only if VDELAY |
is 2 and without separate VBI grabbing. |
We'll have to handle this inside the IRQ handler ... */ |
case V4L2_CID_AUDIO_MUTE: |
c->value = (VIDEO_AUDIO_MUTE & va.flags) ? 1 : 0; |
break; |
case V4L2_CID_AUDIO_VOLUME: |
c->value = va.volume; |
break; |
case V4L2_CID_AUDIO_BALANCE: |
c->value = va.balance; |
break; |
case V4L2_CID_AUDIO_BASS: |
c->value = va.bass; |
break; |
case V4L2_CID_AUDIO_TREBLE: |
c->value = va.treble; |
break; |
|
for (line=0; line < 640; line++) |
{ |
*(ro++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL); |
*(ro++)=cpu_to_le32(virt_to_bus(vadr)); |
*(re++)=cpu_to_le32(BT848_RISC_WRITE|bpl|BT848_RISC_SOL|BT848_RISC_EOL); |
*(re++)=cpu_to_le32(virt_to_bus(vadr+gbufsize/2)); |
vadr+=bpl; |
case V4L2_CID_PRIVATE_CHROMA_AGC: |
c->value = btv->opt_chroma_agc; |
break; |
case V4L2_CID_PRIVATE_COMBFILTER: |
c->value = btv->opt_combfilter; |
break; |
case V4L2_CID_PRIVATE_LUMAFILTER: |
c->value = btv->opt_lumafilter; |
break; |
case V4L2_CID_PRIVATE_AUTOMUTE: |
c->value = btv->opt_automute; |
break; |
case V4L2_CID_PRIVATE_AGC_CRUSH: |
c->value = btv->opt_adc_crush; |
break; |
case V4L2_CID_PRIVATE_VCR_HACK: |
c->value = btv->opt_vcr_hack; |
break; |
default: |
return -EINVAL; |
} |
|
*(ro++)=cpu_to_le32(BT848_RISC_JUMP); |
*(ro++)=cpu_to_le32(btv->bus_vbi_even); |
*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16)); |
*(re++)=cpu_to_le32(btv->bus_vbi_odd); |
|
return 0; |
} |
|
static int make_prisctab(struct bttv *btv, unsigned int *ro, |
unsigned int *re, |
unsigned int *vbuf, unsigned short width, |
unsigned short height, unsigned short fmt) |
static int set_control(struct bttv *btv, struct v4l2_control *c) |
{ |
unsigned long line, lmask; |
unsigned long bl, blcr, blcb, rcmd; |
unsigned long todo; |
unsigned int **rp; |
int inter; |
unsigned long cbadr, cradr; |
unsigned long vadr=(unsigned long) vbuf; |
int shift, csize; |
struct video_audio va; |
int i,val; |
|
if (bttv_debug > 1) |
cprintf("bttv%d: prisc1: ro=%08lx re=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re)); |
|
switch(fmt) |
{ |
case VIDEO_PALETTE_YUV422P: |
csize=(width*height)>>1; |
shift=1; |
lmask=0; |
break; |
|
case VIDEO_PALETTE_YUV411P: |
csize=(width*height)>>2; |
shift=2; |
lmask=0; |
break; |
|
case VIDEO_PALETTE_YUV420P: |
csize=(width*height)>>2; |
shift=1; |
lmask=1; |
break; |
|
case VIDEO_PALETTE_YUV410P: |
csize=(width*height)>>4; |
shift=2; |
lmask=3; |
break; |
|
default: |
return -1; |
for (i = 0; i < BTTV_CTLS; i++) |
if (bttv_ctls[i].id == c->id) |
break; |
if (i == BTTV_CTLS) |
return -EINVAL; |
if (i >= 4 && i <= 8) { |
memset(&va,0,sizeof(va)); |
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); |
if (btv->audio_hook) |
btv->audio_hook(btv,&va,0); |
} |
cbadr=vadr+(width*height); |
cradr=cbadr+csize; |
inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0; |
|
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); |
*(ro++)=0; |
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM3); |
*(re++)=0; |
|
for (line=0; line < (height<<(1^inter)); line++) |
{ |
if(line==height) |
{ |
vadr+=csize<<1; |
cbadr=vadr+(width*height); |
cradr=cbadr+csize; |
switch (c->id) { |
case V4L2_CID_BRIGHTNESS: |
bt848_bright(btv,c->value); |
break; |
case V4L2_CID_HUE: |
bt848_hue(btv,c->value); |
break; |
case V4L2_CID_CONTRAST: |
bt848_contrast(btv,c->value); |
break; |
case V4L2_CID_SATURATION: |
bt848_sat(btv,c->value); |
break; |
case V4L2_CID_AUDIO_MUTE: |
if (c->value) { |
va.flags |= VIDEO_AUDIO_MUTE; |
audio_mux(btv, AUDIO_MUTE); |
} else { |
va.flags &= ~VIDEO_AUDIO_MUTE; |
audio_mux(btv, AUDIO_UNMUTE); |
} |
if (inter) |
rp= (line&1) ? &re : &ro; |
else |
rp= (line>=height) ? &ro : &re; |
|
break; |
|
if(line&lmask) |
rcmd=BT848_RISC_WRITE1S23|BT848_RISC_SOL; |
else |
rcmd=BT848_RISC_WRITE123|BT848_RISC_SOL; |
case V4L2_CID_AUDIO_VOLUME: |
va.volume = c->value; |
break; |
case V4L2_CID_AUDIO_BALANCE: |
va.balance = c->value; |
break; |
case V4L2_CID_AUDIO_BASS: |
va.bass = c->value; |
break; |
case V4L2_CID_AUDIO_TREBLE: |
va.treble = c->value; |
break; |
|
todo=width; |
while(todo) |
{ |
bl=PAGE_SIZE-((PAGE_SIZE-1)&vadr); |
blcr=(PAGE_SIZE-((PAGE_SIZE-1)&cradr))<<shift; |
blcb=(PAGE_SIZE-((PAGE_SIZE-1)&cbadr))<<shift; |
bl=(blcr<bl) ? blcr : bl; |
bl=(blcb<bl) ? blcb : bl; |
bl=(bl>todo) ? todo : bl; |
blcr=bl>>shift; |
blcb=blcr; |
/* bl now containts the longest row that can be written */ |
todo-=bl; |
if(!todo) rcmd|=BT848_RISC_EOL; /* if this is the last EOL */ |
|
*((*rp)++)=cpu_to_le32(rcmd|bl); |
*((*rp)++)=cpu_to_le32(blcb|(blcr<<16)); |
*((*rp)++)=cpu_to_le32(virt_to_bus(vadr)); |
vadr+=bl; |
if((rcmd&(15<<28))==BT848_RISC_WRITE123) |
{ |
*((*rp)++)=cpu_to_le32(virt_to_bus(cbadr)); |
cbadr+=blcb; |
*((*rp)++)=cpu_to_le32(virt_to_bus(cradr)); |
cradr+=blcr; |
} |
|
rcmd&=~BT848_RISC_SOL; /* only the first has SOL */ |
case V4L2_CID_PRIVATE_CHROMA_AGC: |
btv->opt_chroma_agc = c->value; |
val = btv->opt_chroma_agc ? BT848_SCLOOP_CAGC : 0; |
btwrite(val, BT848_E_SCLOOP); |
btwrite(val, BT848_O_SCLOOP); |
break; |
case V4L2_CID_PRIVATE_COMBFILTER: |
btv->opt_combfilter = c->value; |
break; |
case V4L2_CID_PRIVATE_LUMAFILTER: |
btv->opt_lumafilter = c->value; |
if (btv->opt_lumafilter) { |
btand(~BT848_CONTROL_LDEC, BT848_E_CONTROL); |
btand(~BT848_CONTROL_LDEC, BT848_O_CONTROL); |
} else { |
btor(BT848_CONTROL_LDEC, BT848_E_CONTROL); |
btor(BT848_CONTROL_LDEC, BT848_O_CONTROL); |
} |
break; |
case V4L2_CID_PRIVATE_AUTOMUTE: |
btv->opt_automute = c->value; |
break; |
case V4L2_CID_PRIVATE_AGC_CRUSH: |
btv->opt_adc_crush = c->value; |
btwrite(BT848_ADC_RESERVED | (btv->opt_adc_crush ? BT848_ADC_CRUSH : 0), |
BT848_ADC); |
break; |
case V4L2_CID_PRIVATE_VCR_HACK: |
btv->opt_vcr_hack = c->value; |
break; |
default: |
return -EINVAL; |
} |
|
*(ro++)=cpu_to_le32(BT848_RISC_JUMP); |
*(ro++)=cpu_to_le32(btv->bus_vbi_even); |
*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16)); |
*(re++)=cpu_to_le32(btv->bus_vbi_odd); |
|
if (bttv_debug > 1) |
cprintf("bttv%d: prisc2: ro=%08lx re=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re)); |
|
if (i >= 4 && i <= 8) { |
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); |
if (btv->audio_hook) |
btv->audio_hook(btv,&va,1); |
} |
return 0; |
} |
|
static int make_vrisctab(struct bttv *btv, unsigned int *ro, |
unsigned int *re, |
unsigned int *vbuf, unsigned short width, |
unsigned short height, unsigned short palette) |
|
/* ----------------------------------------------------------------------- */ |
|
void bttv_gpio_tracking(struct bttv *btv, char *comment) |
{ |
unsigned long line; |
unsigned long bpl; /* bytes per line */ |
unsigned int **rp; |
int inter; |
unsigned long vadr=(unsigned long) vbuf; |
unsigned int outbits, data; |
outbits = btread(BT848_GPIO_OUT_EN); |
data = btread(BT848_GPIO_DATA); |
printk(KERN_DEBUG "bttv%d: gpio: en=%08x, out=%08x in=%08x [%s]\n", |
btv->nr,outbits,data & outbits, data & ~outbits, comment); |
} |
|
if (palette==VIDEO_PALETTE_RAW) |
return make_rawrisctab(btv, ro, re, vbuf); |
if (palette>=VIDEO_PALETTE_PLANAR) |
return make_prisctab(btv, ro, re, vbuf, width, height, palette); |
void bttv_field_count(struct bttv *btv) |
{ |
int need_count = 0; |
|
if (bttv_debug > 1) |
cprintf("bttv%d: vrisc1: ro=%08lx re=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re)); |
|
inter = (height>tvnorms[btv->win.norm].sheight/2) ? 1 : 0; |
bpl=width*fmtbppx2[palette2fmt[palette]&0xf]/2; |
|
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); |
*(ro++)=cpu_to_le32(0); |
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); |
*(re++)=cpu_to_le32(0); |
|
for (line=0; line < (height<<(1^inter)); line++) |
{ |
if (inter) |
rp= (line&1) ? &re : &ro; |
else |
rp= (line>=height) ? &ro : &re; |
if (btv->users) |
need_count++; |
|
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|BT848_RISC_SOL| |
BT848_RISC_EOL|bpl); |
*((*rp)++)=cpu_to_le32(virt_to_bus(vadr)); |
vadr+=bpl; |
if (need_count) { |
/* start field counter */ |
btor(BT848_INT_VSYNC,BT848_INT_MASK); |
} else { |
/* stop field counter */ |
btand(~BT848_INT_VSYNC,BT848_INT_MASK); |
btv->field_count = 0; |
} |
|
*(ro++)=cpu_to_le32(BT848_RISC_JUMP); |
*(ro++)=cpu_to_le32(btv->bus_vbi_even); |
*(re++)=cpu_to_le32(BT848_RISC_JUMP|BT848_RISC_IRQ|(2<<16)); |
*(re++)=cpu_to_le32(btv->bus_vbi_odd); |
|
if (bttv_debug > 1) |
cprintf("bttv%d: vrisc2: ro=%08lx re=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re)); |
|
return 0; |
} |
|
static unsigned char lmaskt[8] = |
{ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80}; |
static unsigned char rmaskt[8] = |
{ 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; |
|
static void clip_draw_rectangle(unsigned char *clipmap, int x, int y, int w, int h) |
static const struct bttv_format* |
format_by_palette(int palette) |
{ |
unsigned char lmask, rmask, *p; |
int W, l, r; |
int i; |
unsigned int i; |
|
if (bttv_debug > 1) |
cprintf("bttv clip: %dx%d+%d+%d\n",w,h,x,y); |
for (i = 0; i < BTTV_FORMATS; i++) { |
if (-1 == bttv_formats[i].palette) |
continue; |
if (bttv_formats[i].palette == palette) |
return bttv_formats+i; |
} |
return NULL; |
} |
|
/* bitmap is fixed width, 128 bytes (1024 pixels represented) */ |
if (x<0) |
{ |
w+=x; |
x=0; |
} |
if (y<0) |
{ |
h+=y; |
y=0; |
} |
if (w < 0 || h < 0) /* catch bad clips */ |
return; |
/* out of range data should just fall through */ |
if (y+h>=625) |
h=625-y; |
if (x+w>=1024) |
w=1024-x; |
static const struct bttv_format* |
format_by_fourcc(int fourcc) |
{ |
unsigned int i; |
|
l=x>>3; |
r=(x+w-1)>>3; |
W=r-l-1; |
lmask=lmaskt[x&7]; |
rmask=rmaskt[(x+w-1)&7]; |
p=clipmap+128*y+l; |
|
if (W>0) |
{ |
for (i=0; i<h; i++, p+=128) |
{ |
*p|=lmask; |
memset(p+1, 0xff, W); |
p[W+1]|=rmask; |
} |
} else if (!W) { |
for (i=0; i<h; i++, p+=128) |
{ |
p[0]|=lmask; |
p[1]|=rmask; |
} |
} else { |
for (i=0; i<h; i++, p+=128) |
p[0]|=lmask&rmask; |
} |
|
|
for (i = 0; i < BTTV_FORMATS; i++) { |
if (-1 == bttv_formats[i].fourcc) |
continue; |
if (bttv_formats[i].fourcc == fourcc) |
return bttv_formats+i; |
} |
return NULL; |
} |
|
static void make_clip_tab(struct bttv *btv, struct video_clip *cr, int ncr) |
/* ----------------------------------------------------------------------- */ |
/* misc helpers */ |
|
static int |
bttv_switch_overlay(struct bttv *btv, struct bttv_fh *fh, |
struct bttv_buffer *new) |
{ |
int i, line, x, y, bpl, width, height, inter, maxw; |
unsigned int bpp, dx, sx, **rp, *ro, *re, flags, len; |
unsigned long adr; |
unsigned char *clipmap, *clipline, cbit, lastbit, outofmem; |
struct bttv_buffer *old; |
unsigned long flags; |
int retval = 0; |
|
/* take care: bpp != btv->win.bpp is allowed here */ |
bpp = fmtbppx2[btv->win.color_fmt&0xf]/2; |
bpl=btv->win.bpl; |
adr=btv->win.vidadr + btv->win.x * btv->win.bpp + btv->win.y * bpl; |
inter=(btv->win.interlace&1)^1; |
width=btv->win.width; |
height=btv->win.height; |
if (bttv_debug > 1) |
cprintf("bttv%d: clip1: pal=%d size=%dx%d, bpl=%d bpp=%d\n", |
btv->nr,btv->picture.palette,width,height,bpl,bpp); |
if(width > 1023) |
width = 1023; /* sanity check */ |
if(height > 625) |
height = 625; /* sanity check */ |
ro=(unsigned int *)btv->risc_scr_odd; |
re=(unsigned int *)btv->risc_scr_even; |
dprintk("switch_overlay: enter [new=%p]\n",new); |
if (new) |
new->vb.state = STATE_DONE; |
spin_lock_irqsave(&btv->s_lock,flags); |
old = btv->screen; |
btv->screen = new; |
bttv_set_dma(btv, 0x03, 1); |
spin_unlock_irqrestore(&btv->s_lock,flags); |
if (NULL == new) |
free_btres(btv,fh,RESOURCE_OVERLAY); |
if (NULL != old) { |
dprintk("switch_overlay: old=%p state is %d\n",old,old->vb.state); |
bttv_dma_free(btv, old); |
kfree(old); |
} |
dprintk("switch_overlay: done\n"); |
return retval; |
} |
|
if (bttv_debug) |
cprintf("bttv%d: clip: ro=%08lx re=%08lx\n", |
btv->nr,virt_to_bus((unsigned long)ro), virt_to_bus((unsigned long)re)); |
/* ----------------------------------------------------------------------- */ |
/* video4linux (1) interface */ |
|
if ((clipmap=vmalloc_32(VIDEO_CLIPMAP_SIZE))==NULL) { |
/* can't clip, don't generate any risc code */ |
*(ro++)=cpu_to_le32(BT848_RISC_JUMP); |
*(ro++)=cpu_to_le32(btv->bus_vbi_even); |
*(re++)=cpu_to_le32(BT848_RISC_JUMP); |
*(re++)=cpu_to_le32(btv->bus_vbi_odd); |
static int bttv_prepare_buffer(struct bttv *btv, struct bttv_buffer *buf, |
const struct bttv_format *fmt, |
unsigned int width, unsigned int height, |
enum v4l2_field field) |
{ |
int redo_dma_risc = 0; |
int rc; |
|
/* check settings */ |
if (NULL == fmt) |
return -EINVAL; |
if (fmt->btformat == BT848_COLOR_FMT_RAW) { |
width = RAW_BPL; |
height = RAW_LINES*2; |
if (width*height > buf->vb.bsize) |
return -EINVAL; |
buf->vb.size = buf->vb.bsize; |
} else { |
if (width < 48 || |
height < 32 || |
width > bttv_tvnorms[btv->tvnorm].swidth || |
height > bttv_tvnorms[btv->tvnorm].sheight) |
return -EINVAL; |
buf->vb.size = (width * height * fmt->depth) >> 3; |
if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) |
return -EINVAL; |
} |
if (ncr < 0) { /* bitmap was pased */ |
memcpy(clipmap, (unsigned char *)cr, VIDEO_CLIPMAP_SIZE); |
} else { /* convert rectangular clips to a bitmap */ |
memset(clipmap, 0, VIDEO_CLIPMAP_SIZE); /* clear map */ |
for (i=0; i<ncr; i++) |
clip_draw_rectangle(clipmap, cr[i].x, cr[i].y, |
cr[i].width, cr[i].height); |
} |
/* clip against viewing window AND screen |
so we do not have to rely on the user program |
*/ |
maxw = (bpl - btv->win.x * btv->win.bpp) / bpp; |
clip_draw_rectangle(clipmap, (width > maxw) ? maxw : width, |
0, 1024, 768); |
clip_draw_rectangle(clipmap,0,(btv->win.y+height>btv->win.sheight) ? |
(btv->win.sheight-btv->win.y) : height,1024,768); |
if (btv->win.x<0) |
clip_draw_rectangle(clipmap, 0, 0, -(btv->win.x), 768); |
if (btv->win.y<0) |
clip_draw_rectangle(clipmap, 0, 0, 1024, -(btv->win.y)); |
|
*(ro++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); |
*(ro++)=cpu_to_le32(0); |
*(re++)=cpu_to_le32(BT848_RISC_SYNC|BT848_FIFO_STATUS_FM1); |
*(re++)=cpu_to_le32(0); |
|
/* translate bitmap to risc code */ |
for (line=outofmem=0; line < (height<<inter) && !outofmem; line++) |
{ |
y = line>>inter; |
rp= (line&1) ? &re : &ro; |
clipline = clipmap + (y<<7); /* running pointers ... */ |
lastbit = *clipline & 1; |
for(x=dx=0,sx=0; x<=width && !outofmem;) { |
if (0 == (x&7)) { |
/* check bytes not bits if we can ... */ |
if (lastbit) { |
while (0xff==*clipline && x<width-8) { |
x += 8; |
dx += 8; |
clipline++; |
} |
} else { |
while (0x00==*clipline && x<width-8) { |
x += 8; |
dx += 8; |
clipline++; |
} |
} |
} |
cbit = *clipline & (1<<(x&7)); |
if (x < width && !lastbit == !cbit) { |
dx++; |
} else { |
/* generate the dma controller code */ |
len = dx * bpp; |
flags = ((bpp==4) ? BT848_RISC_BYTE3 : 0); |
flags |= ((!sx) ? BT848_RISC_SOL : 0); |
flags |= ((sx + dx == width) ? BT848_RISC_EOL : 0); |
if (!lastbit) { |
*((*rp)++)=cpu_to_le32(BT848_RISC_WRITE|flags|len); |
*((*rp)++)=cpu_to_le32(adr + bpp * sx); |
} else { |
*((*rp)++)=cpu_to_le32(BT848_RISC_SKIP|flags|len); |
} |
lastbit=cbit; |
sx += dx; |
dx = 1; |
if (ro - (unsigned int *)btv->risc_scr_odd>(RISCMEM_LEN>>3) - 16) |
outofmem++; |
if (re - (unsigned int *)btv->risc_scr_even>(RISCMEM_LEN>>3) - 16) |
outofmem++; |
} |
x++; |
if (0 == (x&7)) |
clipline++; |
} |
if ((!inter)||(line&1)) |
adr+=bpl; |
/* alloc + fill struct bttv_buffer (if changed) */ |
if (buf->vb.width != width || buf->vb.height != height || |
buf->vb.field != field || |
buf->tvnorm != btv->tvnorm || buf->fmt != fmt) { |
buf->vb.width = width; |
buf->vb.height = height; |
buf->vb.field = field; |
buf->tvnorm = btv->tvnorm; |
buf->fmt = fmt; |
redo_dma_risc = 1; |
} |
|
vfree_32(clipmap); |
/* outofmem flag relies on the following code to discard extra data */ |
*(ro++)=cpu_to_le32(BT848_RISC_JUMP); |
*(ro++)=cpu_to_le32(btv->bus_vbi_even); |
*(re++)=cpu_to_le32(BT848_RISC_JUMP); |
*(re++)=cpu_to_le32(btv->bus_vbi_odd); |
/* alloc risc memory */ |
if (STATE_NEEDS_INIT == buf->vb.state) { |
redo_dma_risc = 1; |
if (0 != (rc = videobuf_iolock(btv->dev,&buf->vb,&btv->fbuf))) |
goto fail; |
} |
|
if (bttv_debug > 1) |
cprintf("bttv%d: clip2: pal=%d size=%dx%d, bpl=%d bpp=%d\n", |
btv->nr,btv->picture.palette,width,height,bpl,bpp); |
if (redo_dma_risc) |
if (0 != (rc = bttv_buffer_risc(btv,buf))) |
goto fail; |
|
buf->vb.state = STATE_PREPARED; |
return 0; |
|
fail: |
bttv_dma_free(btv,buf); |
return rc; |
} |
|
/* |
* Set the registers for the size we have specified. Don't bother |
* trying to understand this without the BT848 manual in front of |
* you [AC]. |
* |
* PS: The manual is free for download in .pdf format from |
* www.brooktree.com - nicely done those folks. |
*/ |
|
static inline void bt848_set_eogeo(struct bttv *btv, struct tvnorm *tvn, |
int odd, int width, int height) |
static int |
buffer_setup(struct file *file, unsigned int *count, unsigned int *size) |
{ |
u16 vscale, hscale; |
u32 xsf, sr; |
u16 hdelay; |
u8 crop, vtc; |
int inter = (height>tvn->sheight/2) ? 0 : 1; |
int off = odd ? 0x80 : 0x00; |
struct bttv_fh *fh = file->private_data; |
|
*size = fh->fmt->depth*fh->width*fh->height >> 3; |
if (0 == *count) |
*count = gbuffers; |
while (*size * *count > gbuffers * gbufsize) |
(*count)--; |
return 0; |
} |
|
xsf = (width*tvn->scaledtwidth)/tvn->swidth; |
hscale = ((tvn->totalwidth*4096UL)/xsf-4096); |
hdelay = tvn->hdelayx1; |
hdelay = (hdelay*width)/tvn->swidth; |
hdelay &= 0x3fe; |
sr=((tvn->sheight>>inter)*512)/height-512; |
vscale=(0x10000UL-sr)&0x1fff; |
crop=((width>>8)&0x03)|((hdelay>>6)&0x0c)| |
((tvn->sheight>>4)&0x30)|((tvn->vdelay>>2)&0xc0); |
vscale |= inter ? (BT848_VSCALE_INT<<8) : 0; |
static int |
buffer_prepare(struct file *file, struct videobuf_buffer *vb, |
enum v4l2_field field) |
{ |
struct bttv_buffer *buf = (struct bttv_buffer*)vb; |
struct bttv_fh *fh = file->private_data; |
|
if (combfilter) { |
/* Some people say interpolation looks bad ... */ |
vtc = (width < 193) ? 2 : ((width < 385) ? 1 : 0); |
if (width < 769) |
btor(BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); |
else |
btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); |
} else { |
vtc = 0; |
btand(~BT848_VSCALE_COMB, BT848_E_VSCALE_HI+off); |
} |
|
btwrite(vtc, BT848_E_VTC+off); |
btwrite(hscale>>8, BT848_E_HSCALE_HI+off); |
btwrite(hscale&0xff, BT848_E_HSCALE_LO+off); |
btaor((vscale>>8), 0xe0, BT848_E_VSCALE_HI+off); |
btwrite(vscale&0xff, BT848_E_VSCALE_LO+off); |
btwrite(width&0xff, BT848_E_HACTIVE_LO+off); |
btwrite(hdelay&0xff, BT848_E_HDELAY_LO+off); |
btwrite(tvn->sheight&0xff, BT848_E_VACTIVE_LO+off); |
btwrite(tvn->vdelay&0xff, BT848_E_VDELAY_LO+off); |
btwrite(crop, BT848_E_CROP+off); |
return bttv_prepare_buffer(fh->btv, buf, fh->fmt, |
fh->width, fh->height, field); |
} |
|
|
static void bt848_set_geo(struct bttv *btv) |
static void |
buffer_queue(struct file *file, struct videobuf_buffer *vb) |
{ |
u16 ewidth, eheight, owidth, oheight; |
u16 format, bswap; |
struct tvnorm *tvn; |
struct bttv_buffer *buf = (struct bttv_buffer*)vb; |
struct bttv_fh *fh = file->private_data; |
|
tvn=&tvnorms[btv->win.norm]; |
|
btwrite(tvn->adelay, BT848_ADELAY); |
btwrite(tvn->bdelay, BT848_BDELAY); |
btaor(tvn->iform,~(BT848_IFORM_NORM|BT848_IFORM_XTBOTH), BT848_IFORM); |
btwrite(tvn->vbipack, BT848_VBI_PACK_SIZE); |
btwrite(1, BT848_VBI_PACK_DEL); |
buf->vb.state = STATE_QUEUED; |
list_add_tail(&buf->vb.queue,&fh->btv->capture); |
bttv_set_dma(fh->btv, 0x03, 1); |
} |
|
btv->pll.pll_ofreq = tvn->Fsc; |
if (!in_irq) set_pll(btv); |
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; |
|
btv->win.interlace = (btv->win.height>tvn->sheight/2) ? 1 : 0; |
|
if (0 == btv->risc_cap_odd && |
0 == btv->risc_cap_even) { |
/* overlay only */ |
owidth = btv->win.width; |
oheight = btv->win.height; |
ewidth = btv->win.width; |
eheight = btv->win.height; |
format = btv->win.color_fmt; |
bswap = btv->fb_color_ctl; |
} else if (-1 != btv->gq_grab && |
0 == btv->risc_cap_odd && |
!btv->win.interlace && |
btv->scr_on) { |
/* odd field -> overlay, even field -> capture */ |
owidth = btv->win.width; |
oheight = btv->win.height; |
ewidth = btv->gbuf[btv->gq_grab].width; |
eheight = btv->gbuf[btv->gq_grab].height; |
format = (btv->win.color_fmt & 0xf0) | |
(btv->gbuf[btv->gq_grab].fmt & 0x0f); |
bswap = btv->fb_color_ctl & 0x0a; |
} else { |
/* capture only */ |
owidth = btv->gbuf[btv->gq_grab].width; |
oheight = btv->gbuf[btv->gq_grab].height; |
ewidth = btv->gbuf[btv->gq_grab].width; |
eheight = btv->gbuf[btv->gq_grab].height; |
format = btv->gbuf[btv->gq_grab].fmt; |
bswap = 0; |
} |
|
/* program odd + even fields */ |
bt848_set_eogeo(btv, tvn, 1, owidth, oheight); |
bt848_set_eogeo(btv, tvn, 0, ewidth, eheight); |
|
btwrite(format, BT848_COLOR_FMT); |
btwrite(bswap | BT848_COLOR_CTL_GAMMA, BT848_COLOR_CTL); |
bttv_dma_free(fh->btv,buf); |
} |
|
|
static int bpp2fmt[4] = { |
BT848_COLOR_FMT_RGB8, BT848_COLOR_FMT_RGB16, |
BT848_COLOR_FMT_RGB24, BT848_COLOR_FMT_RGB32 |
static struct videobuf_queue_ops bttv_video_qops = { |
.buf_setup = buffer_setup, |
.buf_prepare = buffer_prepare, |
.buf_queue = buffer_queue, |
.buf_release = buffer_release, |
}; |
|
static void bt848_set_winsize(struct bttv *btv) |
static const char *v4l1_ioctls[] = { |
"?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", |
"CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", |
"SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", |
"GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", |
"SMICROCODE", "GVBIFMT", "SVBIFMT" }; |
#define V4L1_IOCTLS ARRAY_SIZE(v4l1_ioctls) |
|
int bttv_common_ioctls(struct bttv *btv, unsigned int cmd, void *arg) |
{ |
unsigned short format; |
switch (cmd) { |
case BTTV_VERSION: |
return BTTV_VERSION_CODE; |
|
if (btv->picture.palette > 0 && btv->picture.palette <= VIDEO_PALETTE_YUV422) { |
/* format set by VIDIOCSPICT */ |
format = palette2fmt[btv->picture.palette]; |
} else { |
/* use default for the given color depth */ |
format = (btv->win.depth==15) ? BT848_COLOR_FMT_RGB15 : |
bpp2fmt[(btv->win.bpp-1)&3]; |
/* *** v4l1 *** ************************************************ */ |
case VIDIOCGFREQ: |
{ |
unsigned long *freq = arg; |
*freq = btv->freq; |
return 0; |
} |
btv->win.color_fmt = format; |
if (bigendian && |
format == BT848_COLOR_FMT_RGB32) { |
btv->fb_color_ctl = |
BT848_COLOR_CTL_WSWAP_ODD | |
BT848_COLOR_CTL_WSWAP_EVEN | |
BT848_COLOR_CTL_BSWAP_ODD | |
BT848_COLOR_CTL_BSWAP_EVEN; |
} else if (bigendian && |
(format == BT848_COLOR_FMT_RGB16 || |
format == BT848_COLOR_FMT_RGB15)) { |
btv->fb_color_ctl = |
BT848_COLOR_CTL_BSWAP_ODD | |
BT848_COLOR_CTL_BSWAP_EVEN; |
} else { |
btv->fb_color_ctl = 0; |
case VIDIOCSFREQ: |
{ |
unsigned long *freq = arg; |
down(&btv->lock); |
btv->freq=*freq; |
bttv_call_i2c_clients(btv,VIDIOCSFREQ,freq); |
if (btv->has_matchbox && btv->radio_user) |
tea5757_set_freq(btv,*freq); |
up(&btv->lock); |
return 0; |
} |
|
/* RGB8 seems to be a 9x5x5 GRB color cube starting at |
* color 16. Why the h... can't they even mention this in the |
* data sheet? [AC - because it's a standard format so I guess |
* it never occurred to them] |
* Enable dithering in this mode. |
*/ |
case VIDIOCGTUNER: |
{ |
struct video_tuner *v = arg; |
|
if (UNSET == bttv_tvcards[btv->type].tuner) |
return -EINVAL; |
if (v->tuner) /* Only tuner 0 */ |
return -EINVAL; |
strcpy(v->name, "Television"); |
v->rangelow = 0; |
v->rangehigh = 0x7FFFFFFF; |
v->flags = VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; |
v->mode = btv->tvnorm; |
v->signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; |
bttv_call_i2c_clients(btv,cmd,v); |
return 0; |
} |
case VIDIOCSTUNER: |
{ |
struct video_tuner *v = arg; |
|
if (format==BT848_COLOR_FMT_RGB8) |
btand(~BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); |
else |
btor(BT848_CAP_CTL_DITH_FRAME, BT848_CAP_CTL); |
if (v->tuner) /* Only tuner 0 */ |
return -EINVAL; |
if (v->mode >= BTTV_TVNORMS) |
return -EINVAL; |
|
bt848_set_geo(btv); |
} |
down(&btv->lock); |
set_tvnorm(btv,v->mode); |
bttv_call_i2c_clients(btv,cmd,v); |
up(&btv->lock); |
return 0; |
} |
|
case VIDIOCGCHAN: |
{ |
struct video_channel *v = arg; |
unsigned int channel = v->channel; |
|
/* |
* Grab into virtual memory. |
*/ |
static int vgrab(struct bttv *btv, struct video_mmap *mp) |
{ |
unsigned int *ro, *re; |
unsigned int *vbuf; |
if (channel >= bttv_tvcards[btv->type].video_inputs) |
return -EINVAL; |
v->tuners=0; |
v->flags = VIDEO_VC_AUDIO; |
v->type = VIDEO_TYPE_CAMERA; |
v->norm = btv->tvnorm; |
if (channel == bttv_tvcards[btv->type].tuner) { |
strcpy(v->name,"Television"); |
v->flags|=VIDEO_VC_TUNER; |
v->type=VIDEO_TYPE_TV; |
v->tuners=1; |
} else if (channel == btv->svhs) { |
strcpy(v->name,"S-Video"); |
} else { |
sprintf(v->name,"Composite%d",channel); |
} |
return 0; |
} |
case VIDIOCSCHAN: |
{ |
struct video_channel *v = arg; |
unsigned int channel = v->channel; |
|
if(btv->fbuffer==NULL) |
return -ENOBUFS; |
if (channel >= bttv_tvcards[btv->type].video_inputs) |
return -EINVAL; |
if (v->norm >= BTTV_TVNORMS) |
return -EINVAL; |
|
if(mp->frame >= gbuffers || mp->frame < 0) |
return -EINVAL; |
|
if(mp->height < 32 || mp->width < 48) |
return -EINVAL; |
if (mp->format >= PALETTEFMT_MAX) |
return -EINVAL; |
down(&btv->lock); |
if (channel == btv->input && |
v->norm == btv->tvnorm) { |
/* nothing to do */ |
up(&btv->lock); |
return 0; |
} |
|
if (mp->height*mp->width*fmtbppx2[palette2fmt[mp->format]&0x0f]/2 |
> gbufsize) |
return -EINVAL; |
if (-1 == palette2fmt[mp->format]) |
return -EINVAL; |
btv->tvnorm = v->norm; |
set_input(btv,v->channel); |
up(&btv->lock); |
return 0; |
} |
|
/* |
* Ok load up the BT848 |
*/ |
|
vbuf=(unsigned int *)(btv->fbuffer+gbufsize*mp->frame); |
ro=(unsigned int *)btv->gbuf[mp->frame].risc; |
re=ro+2048; |
make_vrisctab(btv, ro, re, vbuf, mp->width, mp->height, mp->format); |
case VIDIOCGAUDIO: |
{ |
struct video_audio *v = arg; |
|
if (bttv_debug) |
printk("bttv%d: cap vgrab: queue %d (%d:%dx%d)\n", |
btv->nr,mp->frame,mp->format,mp->width,mp->height); |
btv->gbuf[mp->frame].stat = GBUFFER_GRABBING; |
btv->gbuf[mp->frame].fmt = palette2fmt[mp->format]; |
btv->gbuf[mp->frame].width = mp->width; |
btv->gbuf[mp->frame].height = mp->height; |
btv->gbuf[mp->frame].ro = virt_to_bus((unsigned long)ro); |
btv->gbuf[mp->frame].re = virt_to_bus((unsigned long)re); |
memset(v,0,sizeof(*v)); |
strcpy(v->name,"Television"); |
v->flags |= VIDEO_AUDIO_MUTABLE; |
v->mode = VIDEO_SOUND_MONO; |
|
#if 1 |
if (mp->height <= tvnorms[btv->win.norm].sheight/2 && |
mp->format != VIDEO_PALETTE_RAW) |
btv->gbuf[mp->frame].ro = 0; |
#endif |
down(&btv->lock); |
bttv_call_i2c_clients(btv,cmd,v); |
|
if (-1 == btv->gq_grab && btv->gq_in == btv->gq_out) { |
btv->gq_start = 1; |
btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); |
} |
btv->gqueue[btv->gq_in++] = mp->frame; |
btv->gq_in = btv->gq_in % MAX_GBUFFERS; |
/* card specific hooks */ |
if (btv->audio_hook) |
btv->audio_hook(btv,v,0); |
|
btor(3, BT848_CAP_CTL); |
btor(3, BT848_GPIO_DMA_CTL); |
return 0; |
} |
up(&btv->lock); |
return 0; |
} |
case VIDIOCSAUDIO: |
{ |
struct video_audio *v = arg; |
unsigned int audio = v->audio; |
|
static inline void burst(int on) |
{ |
tvnorms[0].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0); |
tvnorms[0].hdelayx1 = 186 - (on?BURSTOFFSET :0); |
tvnorms[2].scaledtwidth = 1135 - (on?BURSTOFFSET-2:0); |
tvnorms[2].hdelayx1 = 186 - (on?BURSTOFFSET :0); |
} |
if (audio >= bttv_tvcards[btv->type].audio_inputs) |
return -EINVAL; |
|
/* |
* called from irq handler on fatal errors. Takes the grabber chip |
* offline, flag it needs a reinitialization (which can't be done |
* from irq context) and wake up all sleeping proccesses. They would |
* block forever else. We also need someone who actually does the |
* reinitialization from process context... |
*/ |
static void bt848_offline(struct bttv *btv) |
{ |
int i; |
down(&btv->lock); |
audio_mux(btv, (v->flags&VIDEO_AUDIO_MUTE) ? AUDIO_MUTE : AUDIO_UNMUTE); |
bttv_call_i2c_clients(btv,cmd,v); |
|
/* cancel all outstanding grab requests */ |
btv->gq_in = 0; |
btv->gq_out = 0; |
btv->gq_grab = -1; |
for (i = 0; i < gbuffers; i++) |
if (btv->gbuf[i].stat == GBUFFER_GRABBING) |
btv->gbuf[i].stat = GBUFFER_ERROR; |
/* card specific hooks */ |
if (btv->audio_hook) |
btv->audio_hook(btv,v,1); |
|
up(&btv->lock); |
return 0; |
} |
|
/* disable screen overlay and DMA */ |
btv->risc_cap_odd = 0; |
btv->risc_cap_even = 0; |
bt848_set_risc_jmps(btv,0); |
/* *** v4l2 *** ************************************************ */ |
case VIDIOC_ENUMSTD: |
{ |
struct v4l2_standard *e = arg; |
unsigned int index = e->index; |
|
if (index >= BTTV_TVNORMS) |
return -EINVAL; |
v4l2_video_std_construct(e, bttv_tvnorms[e->index].v4l2_id, |
bttv_tvnorms[e->index].name); |
e->index = index; |
return 0; |
} |
case VIDIOC_G_STD: |
{ |
v4l2_std_id *id = arg; |
*id = bttv_tvnorms[btv->tvnorm].v4l2_id; |
return 0; |
} |
case VIDIOC_S_STD: |
{ |
v4l2_std_id *id = arg; |
unsigned int i; |
|
/* flag the chip needs a restart */ |
btv->needs_restart = 1; |
for (i = 0; i < BTTV_TVNORMS; i++) |
if (*id & bttv_tvnorms[i].v4l2_id) |
break; |
if (i == BTTV_TVNORMS) |
return -EINVAL; |
|
} |
down(&btv->lock); |
set_tvnorm(btv,i); |
i2c_vidiocschan(btv); |
up(&btv->lock); |
return 0; |
} |
case VIDIOC_QUERYSTD: |
{ |
v4l2_std_id *id = arg; |
|
if (btread(BT848_DSTATUS) & BT848_DSTATUS_NUML) |
*id = V4L2_STD_625_50; |
else |
*id = V4L2_STD_525_60; |
return 0; |
} |
|
static void bt848_restart(struct bttv *btv) |
{ |
if (bttv_verbose) |
printk(KERN_INFO "bttv%d: resetting chip\n",btv->nr); |
btwrite(0xfffffUL, BT848_INT_STAT); |
btand(~15, BT848_GPIO_DMA_CTL); |
btwrite(0, BT848_SRESET); |
btwrite(virt_to_bus((unsigned long)btv->risc_jmp+8), |
BT848_RISC_STRT_ADD); |
case VIDIOC_ENUMINPUT: |
{ |
struct v4l2_input *i = arg; |
unsigned int n; |
|
n = i->index; |
if (n >= bttv_tvcards[btv->type].video_inputs) |
return -EINVAL; |
memset(i,0,sizeof(*i)); |
i->index = n; |
i->type = V4L2_INPUT_TYPE_CAMERA; |
i->audioset = 1; |
if (i->index == bttv_tvcards[btv->type].tuner) { |
sprintf(i->name, "Television"); |
i->type = V4L2_INPUT_TYPE_TUNER; |
i->tuner = 0; |
} else if (i->index == btv->svhs) { |
sprintf(i->name, "S-Video"); |
} else { |
sprintf(i->name,"Composite%d",i->index); |
} |
if (i->index == btv->input) { |
__u32 dstatus = btread(BT848_DSTATUS); |
if (0 == (dstatus & BT848_DSTATUS_PRES)) |
i->status |= V4L2_IN_ST_NO_SIGNAL; |
if (0 == (dstatus & BT848_DSTATUS_HLOC)) |
i->status |= V4L2_IN_ST_NO_H_LOCK; |
} |
for (n = 0; n < BTTV_TVNORMS; n++) |
i->std |= bttv_tvnorms[n].v4l2_id; |
return 0; |
} |
case VIDIOC_G_INPUT: |
{ |
int *i = arg; |
*i = btv->input; |
return 0; |
} |
case VIDIOC_S_INPUT: |
{ |
unsigned int *i = arg; |
|
if (*i > bttv_tvcards[btv->type].video_inputs) |
return -EINVAL; |
down(&btv->lock); |
set_input(btv,*i); |
i2c_vidiocschan(btv); |
up(&btv->lock); |
return 0; |
} |
|
case VIDIOC_G_TUNER: |
{ |
struct v4l2_tuner *t = arg; |
|
/* enforce pll reprogramming */ |
btv->pll.pll_current = 0; |
set_pll(btv); |
if (UNSET == bttv_tvcards[btv->type].tuner) |
return -EINVAL; |
if (0 != t->index) |
return -EINVAL; |
down(&btv->lock); |
memset(t,0,sizeof(*t)); |
strcpy(t->name, "Television"); |
t->type = V4L2_TUNER_ANALOG_TV; |
t->rangehigh = 0xffffffffUL; |
t->capability = V4L2_TUNER_CAP_NORM; |
t->rxsubchans = V4L2_TUNER_SUB_MONO; |
if (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) |
t->signal = 0xffff; |
{ |
/* Hmmm ... */ |
struct video_audio va; |
memset(&va, 0, sizeof(struct video_audio)); |
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); |
if (btv->audio_hook) |
btv->audio_hook(btv,&va,0); |
if(va.mode & VIDEO_SOUND_STEREO) { |
t->audmode = V4L2_TUNER_MODE_STEREO; |
t->rxsubchans |= V4L2_TUNER_SUB_STEREO; |
} |
if(va.mode & VIDEO_SOUND_LANG1) { |
t->audmode = V4L2_TUNER_MODE_LANG1; |
t->rxsubchans = V4L2_TUNER_SUB_LANG1 |
| V4L2_TUNER_SUB_LANG2; |
} |
} |
/* FIXME: fill capability+audmode */ |
up(&btv->lock); |
return 0; |
} |
case VIDIOC_S_TUNER: |
{ |
struct v4l2_tuner *t = arg; |
|
btv->errors = 0; |
btv->needs_restart = 0; |
bt848_set_geo(btv); |
bt848_set_risc_jmps(btv,-1); |
if (UNSET == bttv_tvcards[btv->type].tuner) |
return -EINVAL; |
if (0 != t->index) |
return -EINVAL; |
down(&btv->lock); |
{ |
struct video_audio va; |
memset(&va, 0, sizeof(struct video_audio)); |
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); |
if (t->audmode == V4L2_TUNER_MODE_MONO) |
va.mode = VIDEO_SOUND_MONO; |
else if (t->audmode == V4L2_TUNER_MODE_STEREO) |
va.mode = VIDEO_SOUND_STEREO; |
else if (t->audmode == V4L2_TUNER_MODE_LANG1) |
va.mode = VIDEO_SOUND_LANG1; |
else if (t->audmode == V4L2_TUNER_MODE_LANG2) |
va.mode = VIDEO_SOUND_LANG2; |
bttv_call_i2c_clients(btv, VIDIOCSAUDIO, &va); |
if (btv->audio_hook) |
btv->audio_hook(btv,&va,1); |
} |
up(&btv->lock); |
return 0; |
} |
|
case VIDIOC_G_FREQUENCY: |
{ |
struct v4l2_frequency *f = arg; |
|
memset(f,0,sizeof(*f)); |
f->type = V4L2_TUNER_ANALOG_TV; |
f->frequency = btv->freq; |
return 0; |
} |
case VIDIOC_S_FREQUENCY: |
{ |
struct v4l2_frequency *f = arg; |
|
if (unlikely(f->tuner != 0)) |
return -EINVAL; |
if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) |
return -EINVAL; |
down(&btv->lock); |
btv->freq = f->frequency; |
bttv_call_i2c_clients(btv,VIDIOCSFREQ,&btv->freq); |
if (btv->has_matchbox && btv->radio_user) |
tea5757_set_freq(btv,btv->freq); |
up(&btv->lock); |
return 0; |
} |
|
default: |
return -ENOIOCTLCMD; |
|
} |
return 0; |
} |
|
/* |
* Open a bttv card. Right now the flags stuff is just playing |
*/ |
|
int bttv_open(struct bttv *btv) |
static int verify_window(const struct bttv_tvnorm *tvn, |
struct v4l2_window *win, int fixup) |
{ |
int i,ret; |
enum v4l2_field field; |
int maxw, maxh; |
|
ret = -EBUSY; |
if (bttv_debug) |
cprintf("bttv%d: open called\n",btv->nr); |
if (win->w.width < 48 || win->w.height < 32) |
return -EINVAL; |
if (win->clipcount > 2048) |
return -EINVAL; |
|
btv->fbuffer=(unsigned char *) vmalloc_32(gbuffers*gbufsize); |
ret = -ENOMEM; |
field = win->field; |
maxw = tvn->swidth; |
maxh = tvn->sheight; |
|
btv->gq_in = 0; |
btv->gq_out = 0; |
btv->gq_grab = -1; |
for (i = 0; i < gbuffers; i++) |
btv->gbuf[i].stat = GBUFFER_UNUSED; |
if (V4L2_FIELD_ANY == field) { |
field = (win->w.height > maxh/2) |
? V4L2_FIELD_INTERLACED |
: V4L2_FIELD_TOP; |
} |
switch (field) { |
case V4L2_FIELD_TOP: |
case V4L2_FIELD_BOTTOM: |
maxh = maxh / 2; |
break; |
case V4L2_FIELD_INTERLACED: |
break; |
default: |
return -EINVAL; |
} |
|
burst(0); |
set_pll(btv); |
btv->user++; |
return 0; |
if (!fixup && (win->w.width > maxw || win->w.height > maxh)) |
return -EINVAL; |
|
if (win->w.width > maxw) |
win->w.width = maxw; |
if (win->w.height > maxh) |
win->w.height = maxh; |
win->field = field; |
return 0; |
} |
|
void bttv_close(struct bttv *btv) |
static int setup_window(struct bttv_fh *fh, struct bttv *btv, |
struct v4l2_window *win, int fixup) |
{ |
int need_wait; |
struct v4l2_clip *clips = NULL; |
int n,size,retval = 0; |
|
handler_remove(btv->dev->irq); |
if (NULL == fh->ovfmt) |
return -EINVAL; |
retval = verify_window(&bttv_tvnorms[btv->tvnorm],win,fixup); |
if (0 != retval) |
return retval; |
|
vfree_32(btv->fbuffer); |
btv->fbuffer = NULL; |
/* copy clips -- luckily v4l1 + v4l2 are binary |
compatible here ...*/ |
n = win->clipcount; |
size = sizeof(*clips)*(n+4); |
clips = kmalloc(size,GFP_KERNEL); |
if (NULL == clips) |
return -ENOMEM; |
if (n > 0) { |
if (copy_from_user(clips,win->clips,sizeof(struct v4l2_clip)*n)) { |
kfree(clips); |
return -EFAULT; |
} |
} |
/* clip against screen */ |
if (NULL != btv->fbuf.base) |
n = btcx_screen_clips(btv->fbuf.fmt.width, btv->fbuf.fmt.height, |
&win->w, clips, n); |
btcx_sort_clips(clips,n); |
|
/* 4-byte alignments */ |
switch (fh->ovfmt->depth) { |
case 8: |
case 24: |
btcx_align(&win->w, clips, n, 3); |
break; |
case 16: |
btcx_align(&win->w, clips, n, 1); |
break; |
case 32: |
/* no alignment fixups needed */ |
break; |
default: |
BUG(); |
} |
|
btv->user--; |
need_wait = (-1 != btv->gq_grab); |
btv->gq_start = 0; |
btv->gq_in = 0; |
btv->gq_out = 0; |
btv->gq_grab = -1; |
btv->scr_on = 0; |
btv->risc_cap_odd = 0; |
btv->risc_cap_even = 0; |
|
pci_write_config_dword(btv->dev,PCI_COMMAND,oldpci_command); |
|
down(&fh->cap.lock); |
if (fh->ov.clips) |
kfree(fh->ov.clips); |
fh->ov.clips = clips; |
fh->ov.nclips = n; |
|
fh->ov.w = win->w; |
fh->ov.field = win->field; |
fh->ov.setup_ok = 1; |
btv->init.ov.w.width = win->w.width; |
btv->init.ov.w.height = win->w.height; |
btv->init.ov.field = win->field; |
|
/* update overlay if needed */ |
retval = 0; |
if (check_btres(fh, RESOURCE_OVERLAY)) { |
struct bttv_buffer *new; |
|
new = videobuf_alloc(sizeof(*new)); |
bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); |
retval = bttv_switch_overlay(btv,fh,new); |
} |
up(&fh->cap.lock); |
return retval; |
} |
|
/***********************************/ |
/* ioctls and supporting functions */ |
/***********************************/ |
/* ----------------------------------------------------------------------- */ |
|
static inline void bt848_bright(struct bttv *btv, uint bright) |
static struct videobuf_queue* bttv_queue(struct bttv_fh *fh) |
{ |
btwrite(bright&0xff, BT848_BRIGHT); |
struct videobuf_queue* q = NULL; |
|
switch (fh->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
q = &fh->cap; |
break; |
case V4L2_BUF_TYPE_VBI_CAPTURE: |
q = &fh->vbi; |
break; |
default: |
BUG(); |
} |
return q; |
} |
|
static inline void bt848_hue(struct bttv *btv, uint hue) |
static int bttv_resource(struct bttv_fh *fh) |
{ |
btwrite(hue&0xff, BT848_HUE); |
int res = 0; |
|
switch (fh->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
res = RESOURCE_VIDEO; |
break; |
case V4L2_BUF_TYPE_VBI_CAPTURE: |
res = RESOURCE_VBI; |
break; |
default: |
BUG(); |
} |
return res; |
} |
|
static inline void bt848_contrast(struct bttv *btv, uint cont) |
static int bttv_switch_type(struct bttv_fh *fh, enum v4l2_buf_type type) |
{ |
unsigned int conthi; |
struct videobuf_queue *q = bttv_queue(fh); |
int res = bttv_resource(fh); |
|
conthi=(cont>>6)&4; |
btwrite(cont&0xff, BT848_CONTRAST_LO); |
btaor(conthi, ~4, BT848_E_CONTROL); |
btaor(conthi, ~4, BT848_O_CONTROL); |
if (check_btres(fh,res)) |
return -EBUSY; |
if (videobuf_queue_is_busy(q)) |
return -EBUSY; |
fh->type = type; |
return 0; |
} |
|
static inline void bt848_sat_u(struct bttv *btv, unsigned long data) |
static int bttv_g_fmt(struct bttv_fh *fh, struct v4l2_format *f) |
{ |
u32 datahi; |
switch (f->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format)); |
f->fmt.pix.width = fh->width; |
f->fmt.pix.height = fh->height; |
f->fmt.pix.field = fh->cap.field; |
f->fmt.pix.pixelformat = fh->fmt->fourcc; |
f->fmt.pix.bytesperline = |
(f->fmt.pix.width * fh->fmt->depth) >> 3; |
f->fmt.pix.sizeimage = |
f->fmt.pix.height * f->fmt.pix.bytesperline; |
return 0; |
case V4L2_BUF_TYPE_VIDEO_OVERLAY: |
memset(&f->fmt.win,0,sizeof(struct v4l2_window)); |
f->fmt.win.w = fh->ov.w; |
f->fmt.win.field = fh->ov.field; |
return 0; |
case V4L2_BUF_TYPE_VBI_CAPTURE: |
bttv_vbi_get_fmt(fh,f); |
return 0; |
default: |
return -EINVAL; |
} |
} |
|
datahi=(data>>7)&2; |
btwrite(data&0xff, BT848_SAT_U_LO); |
btaor(datahi, ~2, BT848_E_CONTROL); |
btaor(datahi, ~2, BT848_O_CONTROL); |
static int bttv_try_fmt(struct bttv_fh *fh, struct bttv *btv, |
struct v4l2_format *f) |
{ |
switch (f->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
{ |
const struct bttv_format *fmt; |
enum v4l2_field field; |
unsigned int maxw,maxh; |
|
fmt = format_by_fourcc(f->fmt.pix.pixelformat); |
if (NULL == fmt) |
return -EINVAL; |
|
/* fixup format */ |
maxw = bttv_tvnorms[btv->tvnorm].swidth; |
maxh = bttv_tvnorms[btv->tvnorm].sheight; |
field = f->fmt.pix.field; |
if (V4L2_FIELD_ANY == field) |
field = (f->fmt.pix.height > maxh/2) |
? V4L2_FIELD_INTERLACED |
: V4L2_FIELD_BOTTOM; |
if (V4L2_FIELD_SEQ_BT == field) |
field = V4L2_FIELD_SEQ_TB; |
switch (field) { |
case V4L2_FIELD_TOP: |
case V4L2_FIELD_BOTTOM: |
case V4L2_FIELD_ALTERNATE: |
maxh = maxh/2; |
break; |
case V4L2_FIELD_INTERLACED: |
break; |
case V4L2_FIELD_SEQ_TB: |
if (fmt->flags & FORMAT_FLAGS_PLANAR) |
return -EINVAL; |
break; |
default: |
return -EINVAL; |
} |
|
/* update data for the application */ |
f->fmt.pix.field = field; |
if (f->fmt.pix.width < 48) |
f->fmt.pix.width = 48; |
if (f->fmt.pix.height < 32) |
f->fmt.pix.height = 32; |
if (f->fmt.pix.width > maxw) |
f->fmt.pix.width = maxw; |
if (f->fmt.pix.height > maxh) |
f->fmt.pix.height = maxh; |
f->fmt.pix.bytesperline = |
(f->fmt.pix.width * fmt->depth) >> 3; |
f->fmt.pix.sizeimage = |
f->fmt.pix.height * f->fmt.pix.bytesperline; |
|
return 0; |
} |
case V4L2_BUF_TYPE_VIDEO_OVERLAY: |
return verify_window(&bttv_tvnorms[btv->tvnorm], |
&f->fmt.win, 1); |
case V4L2_BUF_TYPE_VBI_CAPTURE: |
bttv_vbi_try_fmt(fh,f); |
return 0; |
default: |
return -EINVAL; |
} |
} |
|
static inline void bt848_sat_v(struct bttv *btv, unsigned long data) |
static int bttv_s_fmt(struct bttv_fh *fh, struct bttv *btv, |
struct v4l2_format *f) |
{ |
u32 datahi; |
int retval; |
|
switch (f->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
{ |
const struct bttv_format *fmt; |
|
datahi=(data>>8)&1; |
btwrite(data&0xff, BT848_SAT_V_LO); |
btaor(datahi, ~1, BT848_E_CONTROL); |
btaor(datahi, ~1, BT848_O_CONTROL); |
retval = bttv_switch_type(fh,f->type); |
if (0 != retval) |
return retval; |
retval = bttv_try_fmt(fh,btv,f); |
if (0 != retval) |
return retval; |
fmt = format_by_fourcc(f->fmt.pix.pixelformat); |
|
/* update our state informations */ |
down(&fh->cap.lock); |
fh->fmt = fmt; |
fh->cap.field = f->fmt.pix.field; |
fh->cap.last = V4L2_FIELD_NONE; |
fh->width = f->fmt.pix.width; |
fh->height = f->fmt.pix.height; |
btv->init.fmt = fmt; |
btv->init.width = f->fmt.pix.width; |
btv->init.height = f->fmt.pix.height; |
up(&fh->cap.lock); |
|
return 0; |
} |
case V4L2_BUF_TYPE_VIDEO_OVERLAY: |
return setup_window(fh, btv, &f->fmt.win, 1); |
case V4L2_BUF_TYPE_VBI_CAPTURE: |
retval = bttv_switch_type(fh,f->type); |
if (0 != retval) |
return retval; |
if (locked_btres(fh->btv, RESOURCE_VBI)) |
return -EBUSY; |
bttv_vbi_try_fmt(fh,f); |
bttv_vbi_setlines(fh,btv,f->fmt.vbi.count[0]); |
bttv_vbi_get_fmt(fh,f); |
return 0; |
default: |
return -EINVAL; |
} |
} |
|
/* |
* ioctl routine |
*/ |
|
int bttv_ioctl(struct bttv *btv, unsigned int cmd, void *arg) |
static int bttv_do_ioctl(struct inode *inode, struct file *file, |
unsigned int cmd, void *arg) |
{ |
int i; |
struct bttv_fh *fh = file->private_data; |
struct bttv *btv = fh->btv; |
unsigned long flags; |
int retval = 0; |
|
if (bttv_debug > 1) |
cprintf("bttv%d: ioctl 0x%x\n",btv->nr,cmd); |
if (bttv_debug > 1) { |
switch (_IOC_TYPE(cmd)) { |
case 'v': |
printk("bttv%d: ioctl 0x%x (v4l1, VIDIOC%s)\n", |
btv->nr, cmd, (_IOC_NR(cmd) < V4L1_IOCTLS) ? |
v4l1_ioctls[_IOC_NR(cmd)] : "???"); |
break; |
case 'V': |
printk("bttv%d: ioctl 0x%x (v4l2, %s)\n", |
btv->nr, cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); |
break; |
default: |
printk("bttv%d: ioctl 0x%x ()\n", |
btv->nr, cmd); |
} |
} |
if (btv->errors) |
bttv_reinit_bt848(btv); |
|
#ifdef VIDIOC_G_PRIORITY |
switch (cmd) { |
case VIDIOCSFREQ: |
case VIDIOCSTUNER: |
case VIDIOCSCHAN: |
case VIDIOC_S_CTRL: |
case VIDIOC_S_STD: |
case VIDIOC_S_INPUT: |
case VIDIOC_S_TUNER: |
case VIDIOC_S_FREQUENCY: |
retval = v4l2_prio_check(&btv->prio,&fh->prio); |
if (0 != retval) |
return retval; |
}; |
#endif |
switch (cmd) { |
|
/* *** v4l1 *** ************************************************ */ |
case VIDIOCGCAP: |
{ |
struct video_capability b; |
strcpy(b.name,btv->video_dev.name); |
b.type = VID_TYPE_CAPTURE| |
((bttv_tvcards[btv->type].tuner != -1) ? VID_TYPE_TUNER : 0) | |
VID_TYPE_OVERLAY| |
VID_TYPE_CLIPPING| |
VID_TYPE_FRAMERAM| |
VID_TYPE_SCALES; |
b.channels = bttv_tvcards[btv->type].video_inputs; |
b.audios = bttv_tvcards[btv->type].audio_inputs; |
b.maxwidth = tvnorms[btv->win.norm].swidth; |
b.maxheight = tvnorms[btv->win.norm].sheight; |
b.minwidth = 48; |
b.minheight = 32; |
if(copy_to_user(arg,&b,sizeof(b))) |
return -EFAULT; |
return 0; |
struct video_capability *cap = arg; |
|
memset(cap,0,sizeof(*cap)); |
strcpy(cap->name,btv->video_dev->name); |
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { |
/* vbi */ |
cap->type = VID_TYPE_TUNER|VID_TYPE_TELETEXT; |
} else { |
/* others */ |
cap->type = VID_TYPE_CAPTURE| |
VID_TYPE_TUNER| |
VID_TYPE_OVERLAY| |
VID_TYPE_CLIPPING| |
VID_TYPE_SCALES; |
cap->channels = bttv_tvcards[btv->type].video_inputs; |
cap->audios = bttv_tvcards[btv->type].audio_inputs; |
cap->maxwidth = bttv_tvnorms[btv->tvnorm].swidth; |
cap->maxheight = bttv_tvnorms[btv->tvnorm].sheight; |
cap->minwidth = 48; |
cap->minheight = 32; |
} |
return 0; |
} |
case VIDIOCGCHAN: |
|
case VIDIOCGPICT: |
{ |
struct video_channel v; |
if(copy_from_user(&v, arg,sizeof(v))) |
return -EFAULT; |
v.flags=VIDEO_VC_AUDIO; |
v.tuners=0; |
v.type=VIDEO_TYPE_CAMERA; |
v.norm = btv->win.norm; |
if (v.channel>=bttv_tvcards[btv->type].video_inputs) |
return -EINVAL; |
if(v.channel==bttv_tvcards[btv->type].tuner) |
{ |
strcpy(v.name,"Television"); |
v.flags|=VIDEO_VC_TUNER; |
v.type=VIDEO_TYPE_TV; |
v.tuners=1; |
} |
else if(v.channel==bttv_tvcards[btv->type].svhs) |
strcpy(v.name,"S-Video"); |
else |
sprintf(v.name,"Composite%d",v.channel); |
|
if(copy_to_user(arg,&v,sizeof(v))) |
return -EFAULT; |
struct video_picture *pic = arg; |
|
memset(pic,0,sizeof(*pic)); |
pic->brightness = btv->bright; |
pic->contrast = btv->contrast; |
pic->hue = btv->hue; |
pic->colour = btv->saturation; |
if (fh->fmt) { |
pic->depth = fh->fmt->depth; |
pic->palette = fh->fmt->palette; |
} |
return 0; |
} |
/* |
* Each channel has 1 tuner |
*/ |
case VIDIOCSCHAN: |
case VIDIOCSPICT: |
{ |
struct video_channel v; |
if(copy_from_user(&v, arg,sizeof(v))) |
return -EFAULT; |
struct video_picture *pic = arg; |
const struct bttv_format *fmt; |
|
if (v.channel>bttv_tvcards[btv->type].video_inputs) |
fmt = format_by_palette(pic->palette); |
if (NULL == fmt) |
return -EINVAL; |
if (v.norm > (sizeof(tvnorms)/sizeof(*tvnorms))) |
return -EOPNOTSUPP; |
down(&fh->cap.lock); |
if (fmt->depth != pic->depth && !sloppy) { |
retval = -EINVAL; |
goto fh_unlock_and_return; |
} |
fh->ovfmt = fmt; |
fh->fmt = fmt; |
btv->init.ovfmt = fmt; |
btv->init.fmt = fmt; |
if (bigendian) { |
/* dirty hack time: swap bytes for overlay if the |
display adaptor is big endian (insmod option) */ |
if (fmt->palette == VIDEO_PALETTE_RGB555 || |
fmt->palette == VIDEO_PALETTE_RGB565 || |
fmt->palette == VIDEO_PALETTE_RGB32) { |
fh->ovfmt = fmt+1; |
} |
} |
bt848_bright(btv,pic->brightness); |
bt848_contrast(btv,pic->contrast); |
bt848_hue(btv,pic->hue); |
bt848_sat(btv,pic->colour); |
up(&fh->cap.lock); |
return 0; |
} |
|
bttv_call_i2c_clients(btv,cmd,&v); |
bt848_muxsel(btv, v.channel); |
btv->channel=v.channel; |
if (btv->win.norm != v.norm) { |
if(btv->type== BTTV_VOODOOTV_FM) |
bttv_tda9880_setnorm(btv,v.norm); |
btv->win.norm = v.norm; |
make_vbitab(btv); |
bt848_set_winsize(btv); |
} |
case VIDIOCGWIN: |
{ |
struct video_window *win = arg; |
|
memset(win,0,sizeof(*win)); |
win->x = fh->ov.w.left; |
win->y = fh->ov.w.top; |
win->width = fh->ov.w.width; |
win->height = fh->ov.w.height; |
return 0; |
} |
case VIDIOCGTUNER: |
case VIDIOCSWIN: |
{ |
struct video_tuner v; |
if(copy_from_user(&v,arg,sizeof(v))!=0) |
return -EFAULT; |
#if 0 /* tuner.signal might be of intrest for non-tuner sources too ... */ |
if(v.tuner||btv->channel) /* Only tuner 0 */ |
return -EINVAL; |
#endif |
strcpy(v.name, "Television"); |
v.rangelow=0; |
v.rangehigh=0xFFFFFFFF; |
v.flags=VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; |
v.mode = btv->win.norm; |
v.signal = (btread(BT848_DSTATUS)&BT848_DSTATUS_HLOC) ? 0xFFFF : 0; |
bttv_call_i2c_clients(btv,cmd,&v); |
if(copy_to_user(arg,&v,sizeof(v))) |
return -EFAULT; |
struct video_window *win = arg; |
struct v4l2_window w2; |
|
w2.field = V4L2_FIELD_ANY; |
w2.w.left = win->x; |
w2.w.top = win->y; |
w2.w.width = win->width; |
w2.w.height = win->height; |
w2.clipcount = win->clipcount; |
w2.clips = (struct v4l2_clip*)win->clips; |
retval = setup_window(fh, btv, &w2, 0); |
if (0 == retval) { |
/* on v4l1 this ioctl affects the read() size too */ |
fh->width = fh->ov.w.width; |
fh->height = fh->ov.w.height; |
btv->init.width = fh->ov.w.width; |
btv->init.height = fh->ov.w.height; |
} |
return retval; |
} |
|
case VIDIOCGFBUF: |
{ |
struct video_buffer *fbuf = arg; |
|
fbuf->base = btv->fbuf.base; |
fbuf->width = btv->fbuf.fmt.width; |
fbuf->height = btv->fbuf.fmt.height; |
fbuf->bytesperline = btv->fbuf.fmt.bytesperline; |
if (fh->ovfmt) |
fbuf->depth = fh->ovfmt->depth; |
return 0; |
} |
/* We have but one tuner */ |
case VIDIOCSTUNER: |
case VIDIOCSFBUF: |
{ |
struct video_tuner v; |
if(copy_from_user(&v, arg, sizeof(v))) |
return -EFAULT; |
/* Only one channel has a tuner */ |
if(v.tuner!=bttv_tvcards[btv->type].tuner) |
return -EINVAL; |
|
if(v.mode!=VIDEO_MODE_PAL&&v.mode!=VIDEO_MODE_NTSC |
&&v.mode!=VIDEO_MODE_SECAM) |
return -EOPNOTSUPP; |
bttv_call_i2c_clients(btv,cmd,&v); |
if (btv->win.norm != v.mode) { |
btv->win.norm = v.mode; |
set_pll(btv); |
make_vbitab(btv); |
bt848_set_winsize(btv); |
struct video_buffer *fbuf = arg; |
const struct bttv_format *fmt; |
unsigned long end; |
|
if(!capable(CAP_SYS_ADMIN) && |
!capable(CAP_SYS_RAWIO)) |
return -EPERM; |
end = (unsigned long)fbuf->base + |
fbuf->height * fbuf->bytesperline; |
down(&fh->cap.lock); |
retval = -EINVAL; |
if (sloppy) { |
/* also set the default palette -- for backward |
compatibility with older versions */ |
switch (fbuf->depth) { |
case 8: |
fmt = format_by_palette(VIDEO_PALETTE_HI240); |
break; |
case 16: |
fmt = format_by_palette(VIDEO_PALETTE_RGB565); |
break; |
case 24: |
fmt = format_by_palette(VIDEO_PALETTE_RGB24); |
break; |
case 32: |
fmt = format_by_palette(VIDEO_PALETTE_RGB32); |
break; |
case 15: |
fbuf->depth = 16; |
fmt = format_by_palette(VIDEO_PALETTE_RGB555); |
break; |
default: |
fmt = NULL; |
break; |
} |
if (NULL == fmt) |
goto fh_unlock_and_return; |
fh->ovfmt = fmt; |
fh->fmt = fmt; |
btv->init.ovfmt = fmt; |
btv->init.fmt = fmt; |
} else { |
if (15 == fbuf->depth) |
fbuf->depth = 16; |
if (fbuf->depth != 8 && fbuf->depth != 16 && |
fbuf->depth != 24 && fbuf->depth != 32) |
goto fh_unlock_and_return; |
} |
btv->fbuf.base = fbuf->base; |
btv->fbuf.fmt.width = fbuf->width; |
btv->fbuf.fmt.height = fbuf->height; |
if (fbuf->bytesperline) |
btv->fbuf.fmt.bytesperline = fbuf->bytesperline; |
else |
btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fbuf->depth/8; |
up(&fh->cap.lock); |
return 0; |
} |
case VIDIOCGPICT: |
|
case VIDIOCCAPTURE: |
case VIDIOC_OVERLAY: |
{ |
struct video_picture p=btv->picture; |
if(copy_to_user(arg, &p, sizeof(p))) |
return -EFAULT; |
struct bttv_buffer *new; |
int *on = arg; |
|
if (*on) { |
/* verify args */ |
if (NULL == btv->fbuf.base) |
return -EINVAL; |
if (!fh->ov.setup_ok) { |
dprintk("bttv%d: overlay: !setup_ok\n",btv->nr); |
return -EINVAL; |
} |
} |
|
if (!check_alloc_btres(btv,fh,RESOURCE_OVERLAY)) |
return -EBUSY; |
|
down(&fh->cap.lock); |
if (*on) { |
fh->ov.tvnorm = btv->tvnorm; |
new = videobuf_alloc(sizeof(*new)); |
bttv_overlay_risc(btv, &fh->ov, fh->ovfmt, new); |
} else { |
new = NULL; |
} |
|
/* switch over */ |
retval = bttv_switch_overlay(btv,fh,new); |
up(&fh->cap.lock); |
return retval; |
} |
|
case VIDIOCGMBUF: |
{ |
struct video_mbuf *mbuf = arg; |
unsigned int i; |
|
down(&fh->cap.lock); |
retval = videobuf_mmap_setup(file,&fh->cap,gbuffers,gbufsize, |
V4L2_MEMORY_MMAP); |
if (retval < 0) |
goto fh_unlock_and_return; |
memset(mbuf,0,sizeof(*mbuf)); |
mbuf->frames = gbuffers; |
mbuf->size = gbuffers * gbufsize; |
for (i = 0; i < gbuffers; i++) |
mbuf->offsets[i] = i * gbufsize; |
up(&fh->cap.lock); |
return 0; |
} |
case VIDIOCSPICT: |
case VIDIOCMCAPTURE: |
{ |
struct video_picture p; |
if (copy_from_user(&p, arg,sizeof(p))) |
return -EFAULT; |
if (p.palette > PALETTEFMT_MAX) |
struct video_mmap *vm = arg; |
struct bttv_buffer *buf; |
enum v4l2_field field; |
|
if (vm->frame >= VIDEO_MAX_FRAME) |
return -EINVAL; |
if (-1 == palette2fmt[p.palette]) |
return -EINVAL; |
/* We want -128 to 127 we get 0-65535 */ |
bt848_bright(btv, (p.brightness>>8)-128); |
/* 0-511 for the colour */ |
bt848_sat_u(btv, p.colour>>7); |
bt848_sat_v(btv, ((p.colour>>7)*201L)/237); |
/* -128 to 127 */ |
bt848_hue(btv, (p.hue>>8)-128); |
/* 0-511 */ |
bt848_contrast(btv, p.contrast>>7); |
btv->picture = p; |
|
down(&fh->cap.lock); |
retval = -EINVAL; |
buf = (struct bttv_buffer *)fh->cap.bufs[vm->frame]; |
if (NULL == buf) |
goto fh_unlock_and_return; |
if (0 == buf->vb.baddr) |
goto fh_unlock_and_return; |
if (buf->vb.state == STATE_QUEUED || |
buf->vb.state == STATE_ACTIVE) |
goto fh_unlock_and_return; |
|
field = (vm->height > bttv_tvnorms[btv->tvnorm].sheight/2) |
? V4L2_FIELD_INTERLACED |
: V4L2_FIELD_BOTTOM; |
retval = bttv_prepare_buffer(btv,buf, |
format_by_palette(vm->format), |
vm->width,vm->height,field); |
if (0 != retval) |
goto fh_unlock_and_return; |
spin_lock_irqsave(&btv->s_lock,flags); |
buffer_queue(file,&buf->vb); |
spin_unlock_irqrestore(&btv->s_lock,flags); |
up(&fh->cap.lock); |
return 0; |
} |
case VIDIOCSWIN: |
case VIDIOCSYNC: |
{ |
struct video_window vw; |
struct video_clip *vcp = NULL; |
|
if(copy_from_user(&vw,arg,sizeof(vw))) |
return -EFAULT; |
int *frame = arg; |
struct bttv_buffer *buf; |
|
if(vw.flags || vw.width < 16 || vw.height < 16) |
{ |
btv->scr_on = 0; |
bt848_set_risc_jmps(btv,-1); |
if (*frame >= VIDEO_MAX_FRAME) |
return -EINVAL; |
} |
if (btv->win.bpp < 4) |
{ /* adjust and align writes */ |
vw.x = (vw.x + 3) & ~3; |
vw.width &= ~3; |
} |
if (btv->needs_restart) |
bt848_restart(btv); |
btv->win.x=vw.x; |
btv->win.y=vw.y; |
btv->win.width=vw.width; |
btv->win.height=vw.height; |
|
bt848_set_risc_jmps(btv,0); |
bt848_set_winsize(btv); |
|
/* |
* Do any clips. |
*/ |
if(vw.clipcount<0) { |
if((vcp=vmalloc_32(VIDEO_CLIPMAP_SIZE))==NULL) { |
return -ENOMEM; |
} |
if(copy_from_user(vcp, vw.clips, |
VIDEO_CLIPMAP_SIZE)) { |
vfree_32(vcp); |
return -EFAULT; |
} |
} else if (vw.clipcount > 2048) { |
return -EINVAL; |
} else if (vw.clipcount) { |
if((vcp=vmalloc_32(sizeof(struct video_clip)* |
(vw.clipcount))) == NULL) { |
return -ENOMEM; |
} |
if(copy_from_user(vcp,vw.clips, |
sizeof(struct video_clip)* |
vw.clipcount)) { |
vfree_32(vcp); |
return -EFAULT; |
} |
down(&fh->cap.lock); |
retval = -EINVAL; |
buf = (struct bttv_buffer *)fh->cap.bufs[*frame]; |
if (NULL == buf) |
goto fh_unlock_and_return; |
retval = videobuf_waiton(&buf->vb,0,1); |
if (0 != retval) |
goto fh_unlock_and_return; |
switch (buf->vb.state) { |
case STATE_ERROR: |
retval = -EIO; |
/* fall through */ |
case STATE_DONE: |
videobuf_dma_pci_sync(btv->dev,&buf->vb.dma); |
bttv_dma_free(btv,buf); |
break; |
default: |
retval = -EINVAL; |
break; |
} |
make_clip_tab(btv, vcp, vw.clipcount); |
if (vw.clipcount != 0) |
vfree_32(vcp); |
bt848_set_risc_jmps(btv,-1); |
return 0; |
up(&fh->cap.lock); |
return retval; |
} |
case VIDIOCGWIN: |
|
case VIDIOCGVBIFMT: |
{ |
struct video_window vw; |
memset(&vw,0,sizeof(vw)); |
vw.x=btv->win.x; |
vw.y=btv->win.y; |
vw.width=btv->win.width; |
vw.height=btv->win.height; |
if(btv->win.interlace) |
vw.flags|=VIDEO_WINDOW_INTERLACE; |
if(copy_to_user(arg,&vw,sizeof(vw))) |
return -EFAULT; |
struct vbi_format *fmt = (void *) arg; |
struct v4l2_format fmt2; |
|
if (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) { |
retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); |
if (0 != retval) |
return retval; |
} |
bttv_vbi_get_fmt(fh, &fmt2); |
|
memset(fmt,0,sizeof(*fmt)); |
fmt->sampling_rate = fmt2.fmt.vbi.sampling_rate; |
fmt->samples_per_line = fmt2.fmt.vbi.samples_per_line; |
fmt->sample_format = VIDEO_PALETTE_RAW; |
fmt->start[0] = fmt2.fmt.vbi.start[0]; |
fmt->count[0] = fmt2.fmt.vbi.count[0]; |
fmt->start[1] = fmt2.fmt.vbi.start[1]; |
fmt->count[1] = fmt2.fmt.vbi.count[1]; |
if (fmt2.fmt.vbi.flags & VBI_UNSYNC) |
fmt->flags |= V4L2_VBI_UNSYNC; |
if (fmt2.fmt.vbi.flags & VBI_INTERLACED) |
fmt->flags |= V4L2_VBI_INTERLACED; |
return 0; |
} |
case VIDIOCCAPTURE: |
case VIDIOCSVBIFMT: |
{ |
int v; |
if(copy_from_user(&v, arg,sizeof(v))) |
return -EFAULT; |
if(btv->win.vidadr == 0) |
struct vbi_format *fmt = (void *) arg; |
struct v4l2_format fmt2; |
|
retval = bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); |
if (0 != retval) |
return retval; |
bttv_vbi_get_fmt(fh, &fmt2); |
|
if (fmt->sampling_rate != fmt2.fmt.vbi.sampling_rate || |
fmt->samples_per_line != fmt2.fmt.vbi.samples_per_line || |
fmt->sample_format != VIDEO_PALETTE_RAW || |
fmt->start[0] != fmt2.fmt.vbi.start[0] || |
fmt->start[1] != fmt2.fmt.vbi.start[1] || |
fmt->count[0] != fmt->count[1] || |
fmt->count[0] < 1 || |
fmt->count[0] > 32 /* VBI_MAXLINES */) |
return -EINVAL; |
if (btv->win.width==0 || btv->win.height==0) |
return -EINVAL; |
if (1 == no_overlay) |
return -EIO; |
if (v == 1 && btv->win.vidadr != 0) |
btv->scr_on = 1; |
if (v == 0) |
btv->scr_on = 0; |
bt848_set_risc_jmps(btv,-1); |
|
bttv_vbi_setlines(fh,btv,fmt->count[0]); |
return 0; |
} |
case VIDIOCGFBUF: |
|
case BTTV_VERSION: |
case VIDIOCGFREQ: |
case VIDIOCSFREQ: |
case VIDIOCGTUNER: |
case VIDIOCSTUNER: |
case VIDIOCGCHAN: |
case VIDIOCSCHAN: |
case VIDIOCGAUDIO: |
case VIDIOCSAUDIO: |
return bttv_common_ioctls(btv,cmd,arg); |
|
/* *** v4l2 *** ************************************************ */ |
case VIDIOC_QUERYCAP: |
{ |
struct video_buffer v; |
v.base=(void *)btv->win.vidadr; |
v.height=btv->win.sheight; |
v.width=btv->win.swidth; |
v.depth=btv->win.depth; |
v.bytesperline=btv->win.bpl; |
if(copy_to_user(arg, &v,sizeof(v))) |
return -EFAULT; |
struct v4l2_capability *cap = arg; |
|
if (0 == v4l2) |
return -EINVAL; |
strcpy(cap->driver,"bttv"); |
strlcpy(cap->card,btv->video_dev->name,sizeof(cap->card)); |
sprintf(cap->bus_info,"PCI:%s",pci_name(btv->dev)); |
cap->version = BTTV_VERSION_CODE; |
cap->capabilities = |
V4L2_CAP_VIDEO_CAPTURE | |
V4L2_CAP_VIDEO_OVERLAY | |
V4L2_CAP_VBI_CAPTURE | |
V4L2_CAP_TUNER | |
V4L2_CAP_READWRITE | |
V4L2_CAP_STREAMING; |
return 0; |
|
} |
case VIDIOCSFBUF: |
|
case VIDIOC_ENUM_FMT: |
{ |
struct video_buffer v; |
if(copy_from_user(&v, arg,sizeof(v))) |
return -EFAULT; |
if(v.depth!=8 && v.depth!=15 && v.depth!=16 && |
v.depth!=24 && v.depth!=32 && v.width > 16 && |
v.height > 16 && v.bytesperline > 16) |
struct v4l2_fmtdesc *f = arg; |
enum v4l2_buf_type type; |
unsigned int i; |
int index; |
|
type = f->type; |
if (V4L2_BUF_TYPE_VBI_CAPTURE == type) { |
/* vbi */ |
index = f->index; |
if (0 != index) |
return -EINVAL; |
memset(f,0,sizeof(*f)); |
f->index = index; |
f->type = type; |
f->pixelformat = V4L2_PIX_FMT_GREY; |
strcpy(f->description,"vbi data"); |
return 0; |
} |
|
/* video capture + overlay */ |
index = -1; |
for (i = 0; i < BTTV_FORMATS; i++) { |
if (bttv_formats[i].fourcc != -1) |
index++; |
if ((unsigned int)index == f->index) |
break; |
} |
if (BTTV_FORMATS == i) |
return -EINVAL; |
if (v.base) |
btv->win.vidadr=(unsigned long)v.base; |
btv->win.sheight=v.height; |
btv->win.swidth=v.width; |
btv->win.bpp=((v.depth+7)&0x38)/8; |
btv->win.depth=v.depth; |
btv->win.bpl=v.bytesperline; |
|
#if 0 /* was broken for ages and nobody noticed. Looks like we don't need |
it any more as everybody explicitly sets the palette using VIDIOCSPICT |
these days */ |
/* set sefault color format */ |
switch (v.depth) { |
case 8: btv->picture.palette = VIDEO_PALETTE_HI240; break; |
case 15: btv->picture.palette = VIDEO_PALETTE_RGB555; break; |
case 16: btv->picture.palette = VIDEO_PALETTE_RGB565; break; |
case 24: btv->picture.palette = VIDEO_PALETTE_RGB24; break; |
case 32: btv->picture.palette = VIDEO_PALETTE_RGB32; break; |
switch (f->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
break; |
case V4L2_BUF_TYPE_VIDEO_OVERLAY: |
if (!(bttv_formats[i].flags & FORMAT_FLAGS_PACKED)) |
return -EINVAL; |
break; |
default: |
return -EINVAL; |
} |
#endif |
|
if (bttv_debug) |
cprintf("Display at %p is %d by %d, bytedepth %d, bpl %d\n", |
v.base, v.width,v.height, btv->win.bpp, btv->win.bpl); |
bt848_set_winsize(btv); |
return 0; |
memset(f,0,sizeof(*f)); |
f->index = index; |
f->type = type; |
f->pixelformat = bttv_formats[i].fourcc; |
strlcpy(f->description,bttv_formats[i].name,sizeof(f->description)); |
return 0; |
} |
case VIDIOCKEY: |
|
case VIDIOC_TRY_FMT: |
{ |
/* Will be handled higher up .. */ |
return 0; |
struct v4l2_format *f = arg; |
return bttv_try_fmt(fh,btv,f); |
} |
case VIDIOCGFREQ: |
case VIDIOC_G_FMT: |
{ |
unsigned long v=btv->win.freq; |
if(copy_to_user(arg,&v,sizeof(v))) |
return -EFAULT; |
return 0; |
struct v4l2_format *f = arg; |
return bttv_g_fmt(fh,f); |
} |
case VIDIOCSFREQ: |
case VIDIOC_S_FMT: |
{ |
unsigned long v; |
if(copy_from_user(&v, arg, sizeof(v))) |
return -EFAULT; |
btv->win.freq=v; |
bttv_call_i2c_clients(btv,cmd,&v); |
struct v4l2_format *f = arg; |
return bttv_s_fmt(fh,btv,f); |
} |
|
case VIDIOC_G_FBUF: |
{ |
struct v4l2_framebuffer *fb = arg; |
|
*fb = btv->fbuf; |
fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; |
if (fh->ovfmt) |
fb->fmt.pixelformat = fh->ovfmt->fourcc; |
return 0; |
} |
|
case VIDIOCSYNC: |
case VIDIOC_S_FBUF: |
{ |
return (btv->gbuf[0].stat == GBUFFER_DONE); |
struct v4l2_framebuffer *fb = arg; |
const struct bttv_format *fmt; |
|
} |
if(!capable(CAP_SYS_ADMIN) && |
!capable(CAP_SYS_RAWIO)) |
return -EPERM; |
|
case BTTV_FIELDNR: |
if(copy_to_user((void *) arg, (void *) &btv->last_field, |
sizeof(btv->last_field))) |
return -EFAULT; |
break; |
|
case BTTV_PLLSET: { |
struct bttv_pll_info p; |
if(!capable(CAP_SYS_ADMIN)) |
return -EPERM; |
if(copy_from_user(&p , (void *) arg, sizeof(btv->pll))) |
return -EFAULT; |
btv->pll.pll_ifreq = p.pll_ifreq; |
btv->pll.pll_ofreq = p.pll_ofreq; |
btv->pll.pll_crystal = p.pll_crystal; |
break; |
/* check args */ |
fmt = format_by_fourcc(fb->fmt.pixelformat); |
if (NULL == fmt) |
return -EINVAL; |
if (0 == (fmt->flags & FORMAT_FLAGS_PACKED)) |
return -EINVAL; |
|
down(&fh->cap.lock); |
retval = -EINVAL; |
if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { |
if (fb->fmt.width > bttv_tvnorms[btv->tvnorm].swidth) |
goto fh_unlock_and_return; |
if (fb->fmt.height > bttv_tvnorms[btv->tvnorm].sheight) |
goto fh_unlock_and_return; |
} |
|
/* ok, accept it */ |
btv->fbuf.base = fb->base; |
btv->fbuf.fmt.width = fb->fmt.width; |
btv->fbuf.fmt.height = fb->fmt.height; |
if (0 != fb->fmt.bytesperline) |
btv->fbuf.fmt.bytesperline = fb->fmt.bytesperline; |
else |
btv->fbuf.fmt.bytesperline = btv->fbuf.fmt.width*fmt->depth/8; |
|
retval = 0; |
fh->ovfmt = fmt; |
btv->init.ovfmt = fmt; |
if (fb->flags & V4L2_FBUF_FLAG_OVERLAY) { |
fh->ov.w.left = 0; |
fh->ov.w.top = 0; |
fh->ov.w.width = fb->fmt.width; |
fh->ov.w.height = fb->fmt.height; |
btv->init.ov.w.width = fb->fmt.width; |
btv->init.ov.w.height = fb->fmt.height; |
if (fh->ov.clips) |
kfree(fh->ov.clips); |
fh->ov.clips = NULL; |
fh->ov.nclips = 0; |
|
if (check_btres(fh, RESOURCE_OVERLAY)) { |
struct bttv_buffer *new; |
|
new = videobuf_alloc(sizeof(*new)); |
bttv_overlay_risc(btv,&fh->ov,fh->ovfmt,new); |
retval = bttv_switch_overlay(btv,fh,new); |
} |
} |
up(&fh->cap.lock); |
return retval; |
} |
|
case VIDIOCMCAPTURE: |
case VIDIOC_REQBUFS: |
return videobuf_reqbufs(file,bttv_queue(fh),arg); |
|
case VIDIOC_QUERYBUF: |
return videobuf_querybuf(bttv_queue(fh),arg); |
|
case VIDIOC_QBUF: |
return videobuf_qbuf(file,bttv_queue(fh),arg); |
|
case VIDIOC_DQBUF: |
return videobuf_dqbuf(file,bttv_queue(fh),arg); |
|
case VIDIOC_STREAMON: |
{ |
struct video_mmap vm; |
int ret; |
if(copy_from_user((void *) &vm, (void *) arg, sizeof(vm))) |
return -EFAULT; |
ret = vgrab(btv, &vm); |
return ret; |
int res = bttv_resource(fh); |
|
if (!check_alloc_btres(btv,fh,res)) |
return -EBUSY; |
return videobuf_streamon(file,bttv_queue(fh)); |
} |
|
case VIDIOCGMBUF: |
case VIDIOC_STREAMOFF: |
{ |
struct video_mbuf vm; |
memset(&vm, 0 , sizeof(vm)); |
vm.size=gbufsize*gbuffers; |
vm.frames=gbuffers; |
for (i = 0; i < gbuffers; i++) |
vm.offsets[i]=i*gbufsize; |
if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) |
return -EFAULT; |
int res = bttv_resource(fh); |
|
retval = videobuf_streamoff(file,bttv_queue(fh)); |
if (retval < 0) |
return retval; |
free_btres(btv,fh,res); |
return 0; |
} |
|
case VIDIOCGUNIT: |
|
case VIDIOC_QUERYCTRL: |
{ |
struct video_unit vu; |
vu.video=btv->video_dev.minor; |
vu.vbi=btv->vbi_dev.minor; |
if(btv->radio_dev.minor!=-1) |
vu.radio=btv->radio_dev.minor; |
else |
vu.radio=VIDEO_NO_UNIT; |
vu.audio=VIDEO_NO_UNIT; |
vu.teletext=VIDEO_NO_UNIT; |
if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu))) |
return -EFAULT; |
struct v4l2_queryctrl *c = arg; |
int i; |
|
if ((c->id < V4L2_CID_BASE || |
c->id >= V4L2_CID_LASTP1) && |
(c->id < V4L2_CID_PRIVATE_BASE || |
c->id >= V4L2_CID_PRIVATE_LASTP1)) |
return -EINVAL; |
for (i = 0; i < BTTV_CTLS; i++) |
if (bttv_ctls[i].id == c->id) |
break; |
if (i == BTTV_CTLS) { |
*c = no_ctl; |
return 0; |
} |
*c = bttv_ctls[i]; |
if (i >= 4 && i <= 8) { |
struct video_audio va; |
memset(&va,0,sizeof(va)); |
bttv_call_i2c_clients(btv, VIDIOCGAUDIO, &va); |
if (btv->audio_hook) |
btv->audio_hook(btv,&va,0); |
switch (bttv_ctls[i].id) { |
case V4L2_CID_AUDIO_VOLUME: |
if (!(va.flags & VIDEO_AUDIO_VOLUME)) |
*c = no_ctl; |
break; |
case V4L2_CID_AUDIO_BALANCE: |
if (!(va.flags & VIDEO_AUDIO_BALANCE)) |
*c = no_ctl; |
break; |
case V4L2_CID_AUDIO_BASS: |
if (!(va.flags & VIDEO_AUDIO_BASS)) |
*c = no_ctl; |
break; |
case V4L2_CID_AUDIO_TREBLE: |
if (!(va.flags & VIDEO_AUDIO_TREBLE)) |
*c = no_ctl; |
break; |
} |
} |
return 0; |
} |
|
case BTTV_BURST_ON: |
case VIDIOC_G_CTRL: |
return get_control(btv,arg); |
case VIDIOC_S_CTRL: |
return set_control(btv,arg); |
case VIDIOC_G_PARM: |
{ |
burst(1); |
struct v4l2_streamparm *parm = arg; |
struct v4l2_standard s; |
if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) |
return -EINVAL; |
memset(parm,0,sizeof(*parm)); |
v4l2_video_std_construct(&s, bttv_tvnorms[btv->tvnorm].v4l2_id, |
bttv_tvnorms[btv->tvnorm].name); |
parm->parm.capture.timeperframe = s.frameperiod; |
return 0; |
} |
|
case BTTV_BURST_OFF: |
#ifdef VIDIOC_G_PRIORITY |
case VIDIOC_G_PRIORITY: |
{ |
burst(0); |
return 0; |
} |
enum v4l2_priority *p = arg; |
|
case BTTV_VERSION: |
{ |
*p = v4l2_prio_max(&btv->prio); |
return 0; |
} |
|
case BTTV_PICNR: |
case VIDIOC_S_PRIORITY: |
{ |
/* return picture;*/ |
return 0; |
enum v4l2_priority *prio = arg; |
|
return v4l2_prio_change(&btv->prio, &fh->prio, *prio); |
} |
#endif |
|
|
case VIDIOC_ENUMSTD: |
case VIDIOC_G_STD: |
case VIDIOC_S_STD: |
case VIDIOC_ENUMINPUT: |
case VIDIOC_G_INPUT: |
case VIDIOC_S_INPUT: |
case VIDIOC_G_TUNER: |
case VIDIOC_S_TUNER: |
case VIDIOC_G_FREQUENCY: |
case VIDIOC_S_FREQUENCY: |
return bttv_common_ioctls(btv,cmd,arg); |
|
default: |
return 0; |
return -ENOIOCTLCMD; |
} |
return 0; |
|
fh_unlock_and_return: |
up(&fh->cap.lock); |
return retval; |
} |
|
static void bt848_set_risc_jmps(struct bttv *btv, int flags) |
static int bttv_ioctl(struct inode *inode, struct file *file, |
unsigned int cmd, unsigned long arg) |
{ |
if (-1 == flags) { |
/* defaults */ |
flags = 0; |
if (btv->scr_on) |
flags |= 0x03; |
if (btv->vbi_on) |
flags |= 0x0c; |
#if 0 |
/* Hmm ... */ |
if ((0 != btv->risc_cap_even) || |
(0 != btv->risc_cap_odd)) |
flags |= 0x0c; |
#endif |
struct bttv_fh *fh = file->private_data; |
|
switch (cmd) { |
case BTTV_VBISIZE: |
bttv_switch_type(fh,V4L2_BUF_TYPE_VBI_CAPTURE); |
return fh->lines * 2 * 2048; |
default: |
return video_usercopy(inode, file, cmd, arg, bttv_do_ioctl); |
} |
} |
|
if (bttv_debug > 1) |
cprintf("bttv%d: set_risc_jmp %08lx:", |
btv->nr,virt_to_bus((unsigned long)btv->risc_jmp)); |
static ssize_t bttv_read(struct file *file, char *data, |
size_t count, loff_t *ppos) |
{ |
struct bttv_fh *fh = file->private_data; |
int retval = 0; |
|
/* Sync to start of odd field */ |
btv->risc_jmp[0]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC |
|BT848_FIFO_STATUS_VRE); |
btv->risc_jmp[1]=cpu_to_le32(0); |
if (fh->btv->errors) |
bttv_reinit_bt848(fh->btv); |
dprintk("bttv%d: read count=%d type=%s\n", |
fh->btv->nr,(int)count,v4l2_type_names[fh->type]); |
|
/* Jump to odd vbi sub */ |
btv->risc_jmp[2]=cpu_to_le32(BT848_RISC_JUMP|(0xd<<20)); |
if (flags&8) { |
if (bttv_debug > 1) |
cprintf(" ev=%08lx",virt_to_bus((unsigned long)btv->vbi_odd)); |
btv->risc_jmp[3]=cpu_to_le32(virt_to_bus((unsigned long)btv->vbi_odd)); |
} else { |
if (bttv_debug > 1) |
cprintf(" -----------"); |
btv->risc_jmp[3]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+16)); |
switch (fh->type) { |
case V4L2_BUF_TYPE_VIDEO_CAPTURE: |
if (locked_btres(fh->btv,RESOURCE_VIDEO)) |
return -EBUSY; |
retval = videobuf_read_one(file, &fh->cap, data, count, ppos); |
break; |
case V4L2_BUF_TYPE_VBI_CAPTURE: |
if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) |
return -EBUSY; |
retval = videobuf_read_stream(file, &fh->vbi, data, count, ppos, 1); |
break; |
default: |
BUG(); |
} |
return retval; |
} |
|
/* Jump to odd sub */ |
btv->risc_jmp[4]=cpu_to_le32(BT848_RISC_JUMP|(0xe<<20)); |
if (0 != btv->risc_cap_odd) { |
if (bttv_debug > 1) |
cprintf(" e%d=%08lx",btv->gq_grab,btv->risc_cap_odd); |
flags |= 3; |
btv->risc_jmp[5]=cpu_to_le32(btv->risc_cap_odd); |
} else if ((flags&2) && |
(!btv->win.interlace || 0 == btv->risc_cap_even)) { |
if (bttv_debug > 1) |
cprintf(" eo=%08lx",virt_to_bus((unsigned long)btv->risc_scr_odd)); |
btv->risc_jmp[5]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_scr_odd)); |
static unsigned int bttv_poll(struct file *file, poll_table *wait) |
{ |
struct bttv_fh *fh = file->private_data; |
struct bttv_buffer *buf; |
enum v4l2_field field; |
|
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { |
if (!check_alloc_btres(fh->btv,fh,RESOURCE_VBI)) |
return -EBUSY; |
return videobuf_poll_stream(file, &fh->vbi, wait); |
} |
|
if (check_btres(fh,RESOURCE_VIDEO)) { |
/* streaming capture */ |
if (list_empty(&fh->cap.stream)) |
return POLLERR; |
buf = list_entry(fh->cap.stream.next,struct bttv_buffer,vb.stream); |
} else { |
if (bttv_debug > 1) |
cprintf(" -----------"); |
btv->risc_jmp[5]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+24)); |
/* read() capture */ |
down(&fh->cap.lock); |
if (NULL == fh->cap.read_buf) { |
/* need to capture a new frame */ |
if (locked_btres(fh->btv,RESOURCE_VIDEO)) { |
up(&fh->cap.lock); |
return POLLERR; |
} |
fh->cap.read_buf = videobuf_alloc(fh->cap.msize); |
if (NULL == fh->cap.read_buf) { |
up(&fh->cap.lock); |
return POLLERR; |
} |
fh->cap.read_buf->memory = V4L2_MEMORY_USERPTR; |
field = videobuf_next_field(&fh->cap); |
if (0 != fh->cap.ops->buf_prepare(file,fh->cap.read_buf,field)) { |
up(&fh->cap.lock); |
return POLLERR; |
} |
fh->cap.ops->buf_queue(file,fh->cap.read_buf); |
fh->cap.read_off = 0; |
} |
up(&fh->cap.lock); |
buf = (struct bttv_buffer*)fh->cap.read_buf; |
} |
|
poll_wait(file, &buf->vb.done, wait); |
if (buf->vb.state == STATE_DONE || |
buf->vb.state == STATE_ERROR) |
return POLLIN|POLLRDNORM; |
return 0; |
} |
|
static int bttv_open(struct inode *inode, struct file *file) |
{ |
int minor = iminor(inode); |
struct bttv *btv = NULL; |
struct bttv_fh *fh; |
enum v4l2_buf_type type = 0; |
unsigned int i; |
|
/* Sync to start of even field */ |
btv->risc_jmp[6]=cpu_to_le32(BT848_RISC_SYNC|BT848_RISC_RESYNC |
|BT848_FIFO_STATUS_VRO); |
btv->risc_jmp[7]=cpu_to_le32(0); |
dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor); |
|
/* Jump to even vbi sub */ |
btv->risc_jmp[8]=cpu_to_le32(BT848_RISC_JUMP); |
if (flags&4) { |
if (bttv_debug > 1) |
cprintf(" ov=%08lx",virt_to_bus((unsigned long)btv->vbi_even)); |
btv->risc_jmp[9]=cpu_to_le32(virt_to_bus((unsigned long)btv->vbi_even)); |
} else { |
if (bttv_debug > 1) |
cprintf(" -----------"); |
btv->risc_jmp[9]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+40)); |
for (i = 0; i < bttv_num; i++) { |
if (bttvs[i].video_dev->minor == minor) { |
btv = &bttvs[i]; |
type = V4L2_BUF_TYPE_VIDEO_CAPTURE; |
break; |
} |
if (bttvs[i].vbi_dev->minor == minor) { |
btv = &bttvs[i]; |
type = V4L2_BUF_TYPE_VBI_CAPTURE; |
break; |
} |
} |
if (NULL == btv) |
return -ENODEV; |
|
/* Jump to even sub */ |
btv->risc_jmp[10]=cpu_to_le32(BT848_RISC_JUMP|(8<<20)); |
if (0 != btv->risc_cap_even) { |
if (bttv_debug > 1) |
cprintf(" o%d=%08lx",btv->gq_grab,btv->risc_cap_even); |
flags |= 3; |
btv->risc_jmp[11]=cpu_to_le32(btv->risc_cap_even); |
} else if ((flags&1) && |
btv->win.interlace) { |
if (bttv_debug > 1) |
cprintf(" oo=%08lx",virt_to_bus((unsigned long)btv->risc_scr_even)); |
btv->risc_jmp[11]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_scr_even)); |
} else { |
if (bttv_debug > 1) |
cprintf(" -----------"); |
btv->risc_jmp[11]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp+48)); |
dprintk(KERN_DEBUG "bttv%d: open called (type=%s)\n", |
btv->nr,v4l2_type_names[type]); |
|
/* allocate per filehandle data */ |
fh = kmalloc(sizeof(*fh),GFP_KERNEL); |
if (NULL == fh) |
return -ENOMEM; |
file->private_data = fh; |
*fh = btv->init; |
fh->type = type; |
fh->ov.setup_ok = 0; |
#ifdef VIDIOC_G_PRIORITY |
v4l2_prio_open(&btv->prio,&fh->prio); |
#endif |
|
videobuf_queue_init(&fh->cap, &bttv_video_qops, |
btv->dev, &btv->s_lock, |
V4L2_BUF_TYPE_VIDEO_CAPTURE, |
V4L2_FIELD_INTERLACED, |
sizeof(struct bttv_buffer)); |
videobuf_queue_init(&fh->vbi, &bttv_vbi_qops, |
btv->dev, &btv->s_lock, |
V4L2_BUF_TYPE_VBI_CAPTURE, |
V4L2_FIELD_SEQ_TB, |
sizeof(struct bttv_buffer)); |
i2c_vidiocschan(btv); |
|
btv->users++; |
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) |
bttv_vbi_setlines(fh,btv,16); |
bttv_field_count(btv); |
return 0; |
} |
|
static int bttv_release(struct inode *inode, struct file *file) |
{ |
struct bttv_fh *fh = file->private_data; |
struct bttv *btv = fh->btv; |
|
/* turn off overlay */ |
if (check_btres(fh, RESOURCE_OVERLAY)) |
bttv_switch_overlay(btv,fh,NULL); |
|
/* stop video capture */ |
if (check_btres(fh, RESOURCE_VIDEO)) { |
videobuf_streamoff(file,&fh->cap); |
free_btres(btv,fh,RESOURCE_VIDEO); |
} |
if (fh->cap.read_buf) { |
buffer_release(file,fh->cap.read_buf); |
kfree(fh->cap.read_buf); |
} |
|
if (btv->gq_start) { |
btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP|(0x8<<16)|BT848_RISC_IRQ); |
} else { |
btv->risc_jmp[12]=cpu_to_le32(BT848_RISC_JUMP); |
/* stop vbi capture */ |
if (check_btres(fh, RESOURCE_VBI)) { |
if (fh->vbi.streaming) |
videobuf_streamoff(file,&fh->vbi); |
if (fh->vbi.reading) |
videobuf_read_stop(file,&fh->vbi); |
free_btres(btv,fh,RESOURCE_VBI); |
} |
btv->risc_jmp[13]=cpu_to_le32(virt_to_bus((unsigned long)btv->risc_jmp)); |
|
/* enable cpaturing and DMA */ |
if (bttv_debug > 1) |
cprintf(" flags=0x%x dma=%s\n", |
flags,(flags&0x0f) ? "on" : "off"); |
btaor(flags, ~0x0f, BT848_CAP_CTL); |
if (flags&0x0f) |
bt848_dma(btv, 3); |
else |
bt848_dma(btv, 0); |
#ifdef VIDIOC_G_PRIORITY |
v4l2_prio_close(&btv->prio,&fh->prio); |
#endif |
file->private_data = NULL; |
kfree(fh); |
|
btv->users--; |
bttv_field_count(btv); |
return 0; |
} |
|
static int init_bt848(struct bttv *btv) |
static int |
bttv_mmap(struct file *file, struct vm_area_struct *vma) |
{ |
int j,val; |
struct bttv_fh *fh = file->private_data; |
|
btv->user=0; |
dprintk("bttv%d: mmap type=%s 0x%lx+%ld\n", |
fh->btv->nr, v4l2_type_names[fh->type], |
vma->vm_start, vma->vm_end - vma->vm_start); |
return videobuf_mmap_mapper(vma,bttv_queue(fh)); |
} |
|
/* bringup the gpio internals (quick! before anyone uses it) */ |
init_gpio_adapter(&(btv->gpio_adap)); |
|
/* dump current state of the gpio registers before changing them, |
* might help to make a new card work */ |
if (bttv_gpio) |
bttv_gpio_tracking(btv,"init #1"); |
static struct file_operations bttv_fops = |
{ |
.owner = THIS_MODULE, |
.open = bttv_open, |
.release = bttv_release, |
.ioctl = bttv_ioctl, |
.llseek = no_llseek, |
.read = bttv_read, |
.mmap = bttv_mmap, |
.poll = bttv_poll, |
}; |
|
static struct video_device bttv_video_template = |
{ |
.name = "UNSET", |
.type = VID_TYPE_CAPTURE|VID_TYPE_TUNER|VID_TYPE_OVERLAY| |
VID_TYPE_CLIPPING|VID_TYPE_SCALES, |
.hardware = VID_HARDWARE_BT848, |
.fops = &bttv_fops, |
.minor = -1, |
}; |
|
/* reset the bt848 */ |
btwrite(0, BT848_SRESET); |
DEBUG(printk(KERN_DEBUG "bttv%d: bt848_mem: 0x%lx\n", btv->nr, (unsigned long) btv->bt848_mem)); |
struct video_device bttv_vbi_template = |
{ |
.name = "bt848/878 vbi", |
.type = VID_TYPE_TUNER|VID_TYPE_TELETEXT, |
.hardware = VID_HARDWARE_BT848, |
.fops = &bttv_fops, |
.minor = -1, |
}; |
|
/* not registered yet */ |
btv->video_dev.minor = -1; |
btv->radio_dev.minor = -1; |
btv->vbi_dev.minor = -1; |
/* ----------------------------------------------------------------------- */ |
/* radio interface */ |
|
/* default setup for max. PAL size in a 1024xXXX hicolor framebuffer */ |
btv->win.norm=0; /* change this to 1 for NTSC, 2 for SECAM */ |
btv->win.interlace=1; |
btv->win.x=0; |
btv->win.y=0; |
btv->win.width=320; |
btv->win.height=240; |
btv->win.bpp=2; |
btv->win.depth=16; |
btv->win.color_fmt=BT848_COLOR_FMT_RGB16; |
btv->win.bpl=1024*btv->win.bpp; |
btv->win.swidth=1024; |
btv->win.sheight=768; |
btv->win.vidadr=0; |
btv->vbi_on=0; |
btv->scr_on=0; |
static int radio_open(struct inode *inode, struct file *file) |
{ |
int minor = iminor(inode); |
struct bttv *btv = NULL; |
unsigned int i; |
|
btv->risc_scr_odd=0; |
btv->risc_scr_even=0; |
btv->risc_cap_odd=0; |
btv->risc_cap_even=0; |
btv->risc_jmp=0; |
btv->vbibuf=0; |
btv->field=btv->last_field=0; |
dprintk("bttv: open minor=%d\n",minor); |
|
btv->errors=0; |
btv->needs_restart=0; |
btv->has_radio=radio[btv->nr]; |
for (i = 0; i < bttv_num; i++) { |
if (bttvs[i].radio_dev->minor == minor) { |
btv = &bttvs[i]; |
break; |
} |
} |
if (NULL == btv) |
return -ENODEV; |
|
if (!(btv->risc_scr_odd= (unsigned long *) vmalloc_32(RISCMEM_LEN/2))) |
return -1; |
if (!(btv->risc_scr_even=(unsigned long *) vmalloc_32(RISCMEM_LEN/2))) |
return -1; |
if (!(btv->risc_jmp =(unsigned long *) vmalloc_32(2048))) |
return -1; |
btv->vbi_odd=btv->risc_jmp+64; |
btv->vbi_even=btv->vbi_odd+1024; |
btv->bus_vbi_odd=virt_to_bus((unsigned long)btv->risc_jmp+48); |
btv->bus_vbi_even=virt_to_bus((unsigned long)btv->risc_jmp+24); |
dprintk("bttv%d: open called (radio)\n",btv->nr); |
down(&btv->lock); |
if (btv->radio_user) { |
up(&btv->lock); |
return -EBUSY; |
} |
btv->radio_user++; |
file->private_data = btv; |
|
btwrite(virt_to_bus((unsigned long)btv->risc_jmp+8), BT848_RISC_STRT_ADD); |
btv->vbibuf=(unsigned char *) vmalloc_32(VBIBUF_SIZE); |
if (!btv->vbibuf) |
return -1; |
if (!(btv->gbuf = vmalloc_32(sizeof(struct bttv_gbuf)*gbuffers))) |
return -1; |
for (j = 0; j < gbuffers; j++) { |
if (!(btv->gbuf[j].risc = vmalloc_32(16384))) |
return -1; |
i2c_vidiocschan(btv); |
bttv_call_i2c_clients(btv,AUDC_SET_RADIO,&btv->tuner_type); |
audio_mux(btv,AUDIO_RADIO); |
|
up(&btv->lock); |
return 0; |
} |
|
static int radio_release(struct inode *inode, struct file *file) |
{ |
struct bttv *btv = file->private_data; |
|
btv->radio_user--; |
return 0; |
} |
|
static int radio_do_ioctl(struct inode *inode, struct file *file, |
unsigned int cmd, void *arg) |
{ |
struct bttv *btv = file->private_data; |
|
switch (cmd) { |
case VIDIOCGCAP: |
{ |
struct video_capability *cap = arg; |
|
memset(cap,0,sizeof(*cap)); |
strcpy(cap->name,btv->radio_dev->name); |
cap->type = VID_TYPE_TUNER; |
cap->channels = 1; |
cap->audios = 1; |
return 0; |
} |
|
case VIDIOCGTUNER: |
{ |
struct video_tuner *v = arg; |
|
if(v->tuner) |
return -EINVAL; |
memset(v,0,sizeof(*v)); |
strcpy(v->name, "Radio"); |
/* japan: 76.0 MHz - 89.9 MHz |
western europe: 87.5 MHz - 108.0 MHz |
russia: 65.0 MHz - 108.0 MHz */ |
v->rangelow=(int)(65*16); |
v->rangehigh=(int)(108*16); |
bttv_call_i2c_clients(btv,cmd,v); |
return 0; |
} |
case VIDIOCSTUNER: |
/* nothing to do */ |
return 0; |
|
memset(btv->vbibuf, 0, VBIBUF_SIZE); /* We don't want to return random |
memory to the user */ |
case BTTV_VERSION: |
case VIDIOCGFREQ: |
case VIDIOCSFREQ: |
case VIDIOCGAUDIO: |
case VIDIOCSAUDIO: |
return bttv_common_ioctls(btv,cmd,arg); |
|
btv->fbuffer=NULL; |
default: |
return -ENOIOCTLCMD; |
} |
return 0; |
} |
|
/* btwrite(0, BT848_TDEC); */ |
btwrite(0x10, BT848_COLOR_CTL); |
btwrite(0x00, BT848_CAP_CTL); |
/* set planar and packed mode trigger points and */ |
/* set rising edge of inverted GPINTR pin as irq trigger */ |
btwrite(BT848_GPIO_DMA_CTL_PKTP_32| |
BT848_GPIO_DMA_CTL_PLTP1_16| |
BT848_GPIO_DMA_CTL_PLTP23_16| |
BT848_GPIO_DMA_CTL_GPINTC| |
BT848_GPIO_DMA_CTL_GPINTI, |
BT848_GPIO_DMA_CTL); |
static int radio_ioctl(struct inode *inode, struct file *file, |
unsigned int cmd, unsigned long arg) |
{ |
return video_usercopy(inode, file, cmd, arg, radio_do_ioctl); |
} |
|
/* select direct input */ |
btwrite(0x00, BT848_GPIO_REG_INP); |
btwrite(0x00, BT848_GPIO_OUT_EN); |
if (bttv_gpio) |
bttv_gpio_tracking(btv,"init #2"); |
static struct file_operations radio_fops = |
{ |
.owner = THIS_MODULE, |
.open = radio_open, |
.release = radio_release, |
.ioctl = radio_ioctl, |
.llseek = no_llseek, |
}; |
|
btwrite(BT848_IFORM_MUX1 | BT848_IFORM_XTAUTO | BT848_IFORM_AUTO, |
BT848_IFORM); |
static struct video_device radio_template = |
{ |
.name = "bt848/878 radio", |
.type = VID_TYPE_TUNER, |
.hardware = VID_HARDWARE_BT848, |
.fops = &radio_fops, |
.minor = -1, |
}; |
|
btwrite(0xd8, BT848_CONTRAST_LO); |
bt848_bright(btv, 0x10); |
/* ----------------------------------------------------------------------- */ |
/* irq handler */ |
|
btwrite(0x20, BT848_E_VSCALE_HI); |
btwrite(0x20, BT848_O_VSCALE_HI); |
btwrite(BT848_ADC_RESERVED | (adc_crush ? BT848_ADC_CRUSH : 0), |
BT848_ADC); |
static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK", |
"VPRES", "6", "7", "I2CDONE", "GPINT", "10", |
"RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", |
"RIPERR", "PABORT", "OCERR", "SCERR" }; |
|
if (lumafilter) { |
btwrite(0, BT848_E_CONTROL); |
btwrite(0, BT848_O_CONTROL); |
} else { |
btwrite(BT848_CONTROL_LDEC, BT848_E_CONTROL); |
btwrite(BT848_CONTROL_LDEC, BT848_O_CONTROL); |
static void bttv_print_irqbits(u32 print, u32 mark) |
{ |
unsigned int i; |
|
printk("bits:"); |
for (i = 0; i < ARRAY_SIZE(irq_name); i++) { |
if (print & (1 << i)) |
printk(" %s",irq_name[i]); |
if (mark & (1 << i)) |
printk("*"); |
} |
} |
|
btv->picture.colour=254<<7; |
btv->picture.brightness=128<<8; |
btv->picture.hue=128<<8; |
btv->picture.contrast=0xd8<<7; |
static void bttv_print_riscaddr(struct bttv *btv) |
{ |
printk(" main: %08Lx\n", |
(unsigned long long)btv->main.dma); |
printk(" vbi : o=%08Lx e=%08Lx\n", |
btv->curr.vbi ? (unsigned long long)btv->curr.vbi->top.dma : 0, |
btv->curr.vbi ? (unsigned long long)btv->curr.vbi->bottom.dma : 0); |
printk(" cap : o=%08Lx e=%08Lx\n", |
btv->curr.top ? (unsigned long long)btv->curr.top->top.dma : 0, |
btv->curr.bottom ? (unsigned long long)btv->curr.bottom->bottom.dma : 0); |
printk(" scr : o=%08Lx e=%08Lx\n", |
btv->screen ? (unsigned long long)btv->screen->top.dma : 0, |
btv->screen ? (unsigned long long)btv->screen->bottom.dma : 0); |
} |
|
val = chroma_agc ? BT848_SCLOOP_CAGC : 0; |
btwrite(val, BT848_E_SCLOOP); |
btwrite(val, BT848_O_SCLOOP); |
static int |
bttv_irq_next_set(struct bttv *btv, struct bttv_buffer_set *set) |
{ |
struct bttv_buffer *item; |
|
/* clear interrupt status */ |
btwrite(0xfffffUL, BT848_INT_STAT); |
|
/* set interrupt mask */ |
btwrite(btv->triton1| |
/*BT848_INT_PABORT|BT848_INT_RIPERR|BT848_INT_PPERR| |
BT848_INT_FDSR|BT848_INT_FTRGT|BT848_INT_FBUS|*/ |
(fieldnr ? BT848_INT_VSYNC : 0)| |
BT848_INT_GPINT| |
BT848_INT_SCERR| |
BT848_INT_RISCI|BT848_INT_OCERR|BT848_INT_VPRES| |
BT848_INT_FMTCHG|BT848_INT_HLOCK, |
BT848_INT_MASK); |
memset(set,0,sizeof(*set)); |
|
bt848_muxsel(btv, 1); |
bt848_set_winsize(btv); |
make_vbitab(btv); |
bt848_set_risc_jmps(btv,-1); |
/* vbi request ? */ |
if (!list_empty(&btv->vcapture)) { |
set->irqflags = 1; |
set->vbi = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); |
} |
|
/* needs to be done before i2c is registered */ |
bttv_init_card1(btv); |
/* capture request ? */ |
if (!list_empty(&btv->capture)) { |
set->irqflags = 1; |
item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); |
if (V4L2_FIELD_HAS_TOP(item->vb.field)) |
set->top = item; |
if (V4L2_FIELD_HAS_BOTTOM(item->vb.field)) |
set->bottom = item; |
|
/* register i2c */ |
btv->tuner_type=-1; |
init_bttv_i2c(btv); |
/* capture request for other field ? */ |
if (!V4L2_FIELD_HAS_BOTH(item->vb.field) && |
(item->vb.queue.next != &btv->capture)) { |
item = list_entry(item->vb.queue.next, struct bttv_buffer, vb.queue); |
if (!V4L2_FIELD_HAS_BOTH(item->vb.field)) { |
if (NULL == set->top && |
V4L2_FIELD_TOP == item->vb.field) { |
set->top = item; |
} |
if (NULL == set->bottom && |
V4L2_FIELD_BOTTOM == item->vb.field) { |
set->bottom = item; |
} |
if (NULL != set->top && NULL != set->bottom) |
set->topirq = 2; |
} |
} |
} |
|
/* some card-specific stuff (needs working i2c) */ |
bttv_init_card2(btv); |
/* screen overlay ? */ |
if (NULL != btv->screen) { |
if (V4L2_FIELD_HAS_BOTH(btv->screen->vb.field)) { |
if (NULL == set->top && NULL == set->bottom) { |
set->top = btv->screen; |
set->bottom = btv->screen; |
} |
} else { |
if (V4L2_FIELD_TOP == btv->screen->vb.field && |
NULL == set->top) { |
set->top = btv->screen; |
} |
if (V4L2_FIELD_BOTTOM == btv->screen->vb.field && |
NULL == set->bottom) { |
set->bottom = btv->screen; |
} |
} |
} |
|
dprintk("bttv%d: next set: top=%p bottom=%p vbi=%p " |
"[screen=%p,irq=%d,%d]\n", |
btv->nr,set->top, set->bottom, set->vbi, |
btv->screen,set->irqflags,set->topirq); |
return 0; |
} |
|
/* ----------------------------------------------------------------------- */ |
static void |
bttv_irq_wakeup_set(struct bttv *btv, struct bttv_buffer_set *wakeup, |
struct bttv_buffer_set *curr, unsigned int state) |
{ |
struct timeval ts; |
|
static char *irq_name[] = { "FMTCHG", "VSYNC", "HSYNC", "OFLOW", "HLOCK", |
"VPRES", "6", "7", "I2CDONE", "GPINT", "10", |
"RISCI", "FBUS", "FTRGT", "FDSR", "PPERR", |
"RIPERR", "PABORT", "OCERR", "SCERR" }; |
do_gettimeofday(&ts); |
|
static void bttv_irq(void *arg) |
if (NULL != wakeup->vbi) { |
wakeup->vbi->vb.ts = ts; |
wakeup->vbi->vb.field_count = btv->field_count; |
wakeup->vbi->vb.state = state; |
wake_up(&wakeup->vbi->vb.done); |
} |
if (wakeup->top == wakeup->bottom) { |
if (NULL != wakeup->top && curr->top != wakeup->top) { |
if (irq_debug > 1) |
printk("bttv%d: wakeup: both=%p\n",btv->nr,wakeup->top); |
wakeup->top->vb.ts = ts; |
wakeup->top->vb.field_count = btv->field_count; |
wakeup->top->vb.state = state; |
wake_up(&wakeup->top->vb.done); |
} |
} else { |
if (NULL != wakeup->top && curr->top != wakeup->top) { |
if (irq_debug > 1) |
printk("bttv%d: wakeup: top=%p\n",btv->nr,wakeup->top); |
wakeup->top->vb.ts = ts; |
wakeup->top->vb.field_count = btv->field_count; |
wakeup->top->vb.state = state; |
wake_up(&wakeup->top->vb.done); |
} |
if (NULL != wakeup->bottom && curr->bottom != wakeup->bottom) { |
if (irq_debug > 1) |
printk("bttv%d: wakeup: bottom=%p\n",btv->nr,wakeup->bottom); |
wakeup->bottom->vb.ts = ts; |
wakeup->bottom->vb.field_count = btv->field_count; |
wakeup->bottom->vb.state = state; |
wake_up(&wakeup->bottom->vb.done); |
} |
} |
} |
|
static void bttv_irq_timeout(unsigned long data) |
{ |
struct bttv *btv = (struct bttv *)data; |
struct bttv_buffer_set old,new; |
struct bttv_buffer *item; |
|
if (bttv_verbose) { |
printk(KERN_INFO "bttv%d: timeout: risc=%08x, ", |
btv->nr,btread(BT848_RISC_COUNT)); |
bttv_print_irqbits(btread(BT848_INT_STAT),0); |
printk("\n"); |
} |
|
spin_lock(&btv->s_lock); |
|
/* deactivate stuff */ |
memset(&new,0,sizeof(new)); |
old = btv->curr; |
btv->curr = new; |
bttv_buffer_set_activate(btv, &new); |
bttv_set_dma(btv, 0, 0); |
|
/* wake up */ |
bttv_irq_wakeup_set(btv, &old, &new, STATE_ERROR); |
|
/* cancel all outstanding capture / vbi requests */ |
while (!list_empty(&btv->capture)) { |
item = list_entry(btv->capture.next, struct bttv_buffer, vb.queue); |
list_del(&item->vb.queue); |
item->vb.state = STATE_ERROR; |
wake_up(&item->vb.done); |
} |
while (!list_empty(&btv->vcapture)) { |
item = list_entry(btv->vcapture.next, struct bttv_buffer, vb.queue); |
list_del(&item->vb.queue); |
item->vb.state = STATE_ERROR; |
wake_up(&item->vb.done); |
} |
|
btv->errors++; |
spin_unlock(&btv->s_lock); |
} |
|
static void |
bttv_irq_wakeup_top(struct bttv *btv) |
{ |
struct bttv_buffer *wakeup = btv->curr.top; |
|
if (NULL == wakeup) |
return; |
|
spin_lock(&btv->s_lock); |
btv->curr.topirq = 0; |
btv->curr.top = NULL; |
bttv_risc_hook(btv, RISC_SLOT_O_FIELD, NULL, 0); |
|
do_gettimeofday(&wakeup->vb.ts); |
wakeup->vb.field_count = btv->field_count; |
wakeup->vb.state = STATE_DONE; |
wake_up(&wakeup->vb.done); |
spin_unlock(&btv->s_lock); |
} |
|
static void |
bttv_irq_switch_fields(struct bttv *btv) |
{ |
struct bttv_buffer_set new; |
struct bttv_buffer_set old; |
dma_addr_t rc; |
|
spin_lock(&btv->s_lock); |
|
/* new buffer set */ |
bttv_irq_next_set(btv, &new); |
rc = btread(BT848_RISC_COUNT); |
if (rc < btv->main.dma || rc > btv->main.dma + 0x100) { |
if (1 /* irq_debug */) |
printk("bttv%d: skipped frame. no signal? high irq latency?\n", |
btv->nr); |
spin_unlock(&btv->s_lock); |
return; |
} |
|
/* switch over */ |
old = btv->curr; |
btv->curr = new; |
bttv_buffer_set_activate(btv, &new); |
bttv_set_dma(btv, 0, new.irqflags); |
|
/* switch input */ |
if (UNSET != btv->new_input) { |
video_mux(btv,btv->new_input); |
btv->new_input = UNSET; |
} |
|
/* wake up finished buffers */ |
bttv_irq_wakeup_set(btv, &old, &new, STATE_DONE); |
spin_unlock(&btv->s_lock); |
} |
|
static irqreturn_t bttv_irq(int irq, void *dev_id, struct pt_regs * regs) |
{ |
u32 stat,astat; |
u32 dstat; |
int count; |
struct bttv *btv = btvirq; |
struct bttv *btv; |
int handled = 0; |
|
cli(); |
|
in_irq = 1; |
btv=(struct bttv *)dev_id; |
count=0; |
while (1) |
{ |
while (1) { |
/* get/clear interrupt status bits */ |
stat=btread(BT848_INT_STAT); |
astat=stat&btread(BT848_INT_MASK); |
if (!astat) { |
in_irq = 0; |
sti(); |
return; |
} |
if (!astat) |
break; |
handled = 1; |
btwrite(stat,BT848_INT_STAT); |
|
/* get device status bits */ |
1896,236 → 3369,447 |
dstat=btread(BT848_DSTATUS); |
|
if (irq_debug) { |
int i; |
cprintf("bttv%d: irq loop=%d risc=%lx, bits:", |
btv->nr, count, stat>>28); |
for (i = 0; i < (sizeof(irq_name)/sizeof(char*)); i++) { |
if (stat & (1 << i)) |
cprintf(" %s",irq_name[i]); |
if (astat & (1 << i)) |
cprintf(" *"); |
} |
printk(KERN_DEBUG "bttv%d: irq loop=%d fc=%d " |
"riscs=%x, riscc=%08x, ", |
btv->nr, count, btv->field_count, |
stat>>28, btread(BT848_RISC_COUNT)); |
bttv_print_irqbits(stat,astat); |
if (stat & BT848_INT_HLOCK) |
cprintf(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC) |
printk(" HLOC => %s", (dstat & BT848_DSTATUS_HLOC) |
? "yes" : "no"); |
if (stat & BT848_INT_VPRES) |
cprintf(" PRES => %s", (dstat & BT848_DSTATUS_PRES) |
printk(" PRES => %s", (dstat & BT848_DSTATUS_PRES) |
? "yes" : "no"); |
if (stat & BT848_INT_FMTCHG) |
cprintf(" NUML => %s", (dstat & BT848_DSTATUS_PRES) |
printk(" NUML => %s", (dstat & BT848_DSTATUS_NUML) |
? "625" : "525"); |
cprintf("\n"); |
printk("\n"); |
} |
|
if (astat&BT848_INT_VSYNC) |
btv->field++; |
btv->field_count++; |
|
if (astat&(BT848_INT_SCERR|BT848_INT_OCERR)) { |
if (bttv_verbose) |
cprintf("bttv%d: irq:%s%s risc_count=%08lx\n", |
btv->nr, |
(astat&BT848_INT_SCERR) ? " SCERR" : "", |
(astat&BT848_INT_OCERR) ? " OCERR" : "", |
btread(BT848_RISC_COUNT)); |
btv->errors++; |
if (btv->errors < BTTV_ERRORS) { |
btand(~15, BT848_GPIO_DMA_CTL); |
btwrite(virt_to_bus((unsigned long)(btv->risc_jmp+8)), |
BT848_RISC_STRT_ADD); |
bt848_set_geo(btv); |
bt848_set_risc_jmps(btv,-1); |
} else { |
if (bttv_verbose) |
cprintf("bttv%d: aiee: error loops\n",btv->nr); |
bt848_offline(btv); |
} |
if (astat & BT848_INT_GPINT) { |
#ifdef CONFIG_VIDEO_IR |
if (btv->remote) |
bttv_input_irq(btv); |
#endif |
wake_up(&btv->gpioq); |
} |
if (astat&BT848_INT_RISCI) |
{ |
if (bttv_debug > 1) |
cprintf("bttv%d: IRQ_RISCI\n",btv->nr); |
|
/* captured VBI frame */ |
if (stat&(1<<28)) |
{ |
btv->vbip=0; |
/* inc vbi frame count for detecting drops */ |
(*(u32 *)&(btv->vbibuf[VBIBUF_SIZE - 4]))++; |
} |
if ((astat & BT848_INT_RISCI) && (stat & (2<<28))) |
bttv_irq_wakeup_top(btv); |
|
/* captured full frame */ |
if (stat&(2<<28) && btv->gq_grab != -1) |
{ |
btv->last_field=btv->field; |
if (bttv_debug) |
cprintf("bttv%d: cap irq: done %d\n",btv->nr,btv->gq_grab); |
btv->gbuf[btv->gq_grab].stat = GBUFFER_DONE; |
btv->gq_grab = -1; |
if (btv->gq_in != btv->gq_out) |
{ |
btv->gq_grab = btv->gqueue[btv->gq_out++]; |
btv->gq_out = btv->gq_out % MAX_GBUFFERS; |
if (bttv_debug) |
cprintf("bttv%d: cap irq: capture %d\n",btv->nr,btv->gq_grab); |
btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro; |
btv->risc_cap_even = btv->gbuf[btv->gq_grab].re; |
bt848_set_risc_jmps(btv,-1); |
bt848_set_geo(btv); |
btwrite(BT848_COLOR_CTL_GAMMA, |
BT848_COLOR_CTL); |
} else { |
btv->risc_cap_odd = 0; |
btv->risc_cap_even = 0; |
bt848_set_risc_jmps(btv,-1); |
bt848_set_geo(btv); |
btwrite(btv->fb_color_ctl | BT848_COLOR_CTL_GAMMA, |
BT848_COLOR_CTL); |
} |
break; |
} |
if (stat&(8<<28) && btv->gq_start) |
{ |
btv->gq_start = 0; |
btv->gq_grab = btv->gqueue[btv->gq_out++]; |
btv->gq_out = btv->gq_out % MAX_GBUFFERS; |
if (bttv_debug) |
cprintf("bttv%d: cap irq: capture %d [start]\n",btv->nr,btv->gq_grab); |
btv->risc_cap_odd = btv->gbuf[btv->gq_grab].ro; |
btv->risc_cap_even = btv->gbuf[btv->gq_grab].re; |
bt848_set_risc_jmps(btv,-1); |
bt848_set_geo(btv); |
btwrite(BT848_COLOR_CTL_GAMMA, |
BT848_COLOR_CTL); |
} |
if ((astat & BT848_INT_RISCI) && (stat & (1<<28))) |
bttv_irq_switch_fields(btv); |
|
if ((astat & BT848_INT_HLOCK) && btv->opt_automute) |
audio_mux(btv, -1); |
|
if (astat & (BT848_INT_SCERR|BT848_INT_OCERR)) { |
printk(KERN_INFO "bttv%d: %s%s @ %08x,",btv->nr, |
(astat & BT848_INT_SCERR) ? "SCERR" : "", |
(astat & BT848_INT_OCERR) ? "OCERR" : "", |
btread(BT848_RISC_COUNT)); |
bttv_print_irqbits(stat,astat); |
printk("\n"); |
if (bttv_debug) |
bttv_print_riscaddr(btv); |
} |
if (fdsr && astat & BT848_INT_FDSR) { |
printk(KERN_INFO "bttv%d: FDSR @ %08x\n", |
btv->nr,btread(BT848_RISC_COUNT)); |
if (bttv_debug) |
bttv_print_riscaddr(btv); |
} |
|
count++; |
if (count > 20) { |
if (count > 4) { |
btwrite(0, BT848_INT_MASK); |
cprintf("bttv%d: IRQ lockup, cleared int mask\n", btv->nr); |
bt848_offline(btv); |
printk(KERN_ERR |
"bttv%d: IRQ lockup, cleared int mask [", btv->nr); |
bttv_print_irqbits(stat,astat); |
printk("]\n"); |
} |
} |
return IRQ_RETVAL(handled); |
} |
|
in_irq = 0; |
|
sti(); |
/* ----------------------------------------------------------------------- */ |
/* initialitation */ |
|
static struct video_device *vdev_init(struct bttv *btv, |
struct video_device *template, |
char *type) |
{ |
struct video_device *vfd; |
|
vfd = video_device_alloc(); |
if (NULL == vfd) |
return NULL; |
*vfd = *template; |
vfd->minor = -1; |
vfd->dev = &btv->dev->dev; |
vfd->release = video_device_release; |
snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)", |
btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "", |
type, bttv_tvcards[btv->type].name); |
return vfd; |
} |
|
static void bttv_unregister_video(struct bttv *btv) |
{ |
if (btv->video_dev) { |
if (-1 != btv->video_dev->minor) |
video_unregister_device(btv->video_dev); |
else |
video_device_release(btv->video_dev); |
btv->video_dev = NULL; |
} |
if (btv->vbi_dev) { |
if (-1 != btv->vbi_dev->minor) |
video_unregister_device(btv->vbi_dev); |
else |
video_device_release(btv->vbi_dev); |
btv->vbi_dev = NULL; |
} |
if (btv->radio_dev) { |
if (-1 != btv->radio_dev->minor) |
video_unregister_device(btv->radio_dev); |
else |
video_device_release(btv->radio_dev); |
btv->radio_dev = NULL; |
} |
} |
|
/* register video4linux devices */ |
static int __devinit bttv_register_video(struct bttv *btv) |
{ |
/* video */ |
btv->video_dev = vdev_init(btv, &bttv_video_template, "video"); |
if (NULL == btv->video_dev) |
goto err; |
if (video_register_device(btv->video_dev,VFL_TYPE_GRABBER,video_nr)<0) |
goto err; |
printk(KERN_INFO "bttv%d: registered device video%d\n", |
btv->nr,btv->video_dev->minor & 0x1f); |
video_device_create_file(btv->video_dev, &class_device_attr_card); |
|
/* vbi */ |
btv->vbi_dev = vdev_init(btv, &bttv_vbi_template, "vbi"); |
if (NULL == btv->vbi_dev) |
goto err; |
if (video_register_device(btv->vbi_dev,VFL_TYPE_VBI,vbi_nr)<0) |
goto err; |
printk(KERN_INFO "bttv%d: registered device vbi%d\n", |
btv->nr,btv->vbi_dev->minor & 0x1f); |
|
if (!btv->has_radio) |
return 0; |
/* radio */ |
btv->radio_dev = vdev_init(btv, &radio_template, "radio"); |
if (NULL == btv->radio_dev) |
goto err; |
if (video_register_device(btv->radio_dev, VFL_TYPE_RADIO,radio_nr)<0) |
goto err; |
printk(KERN_INFO "bttv%d: registered device radio%d\n", |
btv->nr,btv->radio_dev->minor & 0x1f); |
|
/* all done */ |
return 0; |
|
err: |
bttv_unregister_video(btv); |
return -1; |
} |
|
|
/* on OpenFirmware machines (PowerMac at least), PCI memory cycle */ |
/* response on cards with no firmware is not enabled by OF */ |
static void pci_set_command(struct pci_dev *dev) |
{ |
#if defined(__powerpc__) |
unsigned int cmd; |
|
pci_read_config_dword(dev, PCI_COMMAND, &cmd); |
cmd = (cmd | PCI_COMMAND_MEMORY ); |
pci_write_config_dword(dev, PCI_COMMAND, cmd); |
#endif |
} |
|
int bttv_probe(struct bttv *btv) |
static int __devinit bttv_probe(struct pci_dev *dev, |
const struct pci_device_id *pci_id) |
{ |
int result; |
unsigned char lat; |
|
struct bttv *btv; |
|
if (bttv_num == BTTV_MAX) |
return -ENOMEM; |
printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num); |
btv=&bttvs[bttv_num]; |
memset(btv,0,sizeof(*btv)); |
btv->nr = bttv_num; |
sprintf(btv->name,"bttv%d",btv->nr); |
|
btvirq = btv; |
btv->nr = bttv_num; |
//because not all compilers allow anonymous unions duplication is needed |
btv->gpio_adap.bt848_mem = btv->bt848_mem = NULL; |
btv->vbibuf=NULL; |
btv->risc_jmp=NULL; |
btv->vbi_odd=NULL; |
btv->vbi_even=NULL; |
btv->vbip=VBIBUF_SIZE; |
btv->shutdown=0; |
/* initialize structs / fill in defaults */ |
init_MUTEX(&btv->lock); |
init_MUTEX(&btv->reslock); |
btv->s_lock = SPIN_LOCK_UNLOCKED; |
init_waitqueue_head(&btv->gpioq); |
INIT_LIST_HEAD(&btv->capture); |
INIT_LIST_HEAD(&btv->vcapture); |
#ifdef VIDIOC_G_PRIORITY |
v4l2_prio_init(&btv->prio); |
#endif |
|
btv->id = 848; |
btv->bt848_adr=pci_resource_start(btv->dev,0); |
if (btv->id >= 878) |
btv->i2c_command = 0x83; |
else |
btv->i2c_command=(I2C_TIMING | BT848_I2C_SCL | BT848_I2C_SDA); |
init_timer(&btv->timeout); |
btv->timeout.function = bttv_irq_timeout; |
btv->timeout.data = (unsigned long)btv; |
|
btv->i2c_rc = -1; |
btv->tuner_type = UNSET; |
btv->pinnacle_id = UNSET; |
btv->new_input = UNSET; |
btv->has_radio=radio[btv->nr]; |
|
/* pci stuff (init, get irq/mmio, ... */ |
btv->dev = dev; |
btv->id = dev->device; |
if (pci_enable_device(dev)) { |
printk(KERN_WARNING "bttv%d: Can't enable device.\n", |
btv->nr); |
return -EIO; |
} |
if (pci_set_dma_mask(dev, 0xffffffff)) { |
printk(KERN_WARNING "bttv%d: No suitable DMA available.\n", |
btv->nr); |
return -EIO; |
} |
if (!request_mem_region(pci_resource_start(dev,0), |
pci_resource_len(dev,0), |
btv->name)) { |
printk(KERN_WARNING "bttv%d: can't request iomem (0x%lx).\n", |
btv->nr, pci_resource_start(dev,0)); |
return -EBUSY; |
} |
pci_set_master(dev); |
pci_set_command(dev); |
pci_set_drvdata(dev,btv); |
if (!pci_dma_supported(dev,0xffffffff)) { |
printk("bttv%d: Oops: no 32bit PCI DMA ???\n", btv->nr); |
result = -EIO; |
goto fail1; |
} |
|
if (-1 != latency) { |
printk(KERN_INFO "bttv%d: setting pci latency timer to %d\n", |
bttv_num,latency); |
pci_write_config_byte(btv->dev, PCI_LATENCY_TIMER, latency); |
pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision); |
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat); |
printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ", |
bttv_num,btv->id, btv->revision, pci_name(dev)); |
printk("irq: %d, latency: %d, mmio: 0x%lx\n", |
btv->dev->irq, lat, pci_resource_start(dev,0)); |
schedule(); |
|
btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000); |
if (NULL == ioremap(pci_resource_start(dev,0), 0x1000)) { |
printk("bttv%d: ioremap() failed\n", btv->nr); |
result = -EIO; |
goto fail1; |
} |
pci_read_config_byte(btv->dev, PCI_CLASS_REVISION, &btv->revision); |
pci_read_config_byte(btv->dev, PCI_LATENCY_TIMER, &lat); |
printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %2x:%2x\n", bttv_num,btv->id, btv->revision, btv->dev->bus->number, btv->dev->devfn); |
printk(KERN_INFO "irq: %d, latency: %d, mmio: 0x%lx\n", |
btv->dev->irq, lat, btv->bt848_adr); |
|
btv->bt848_mem = (void *)btv->bt848_adr; |
btv->gpio_adap.bt848_mem = btv->bt848_mem; |
|
btv->pll.pll_crystal = 0; |
|
/* identify card */ |
bttv_idcard(btv); |
|
/* clear interrupt mask */ |
/* disable irqs, register irq handler */ |
btwrite(0, BT848_INT_MASK); |
result = request_irq(btv->dev->irq, bttv_irq, |
SA_SHIRQ | SA_INTERRUPT,btv->name,(void *)btv); |
if (result < 0) { |
printk(KERN_ERR "bttv%d: can't get IRQ %d\n", |
bttv_num,btv->dev->irq); |
goto fail1; |
} |
|
handler_set(btv->dev->irq, (void (*)(int)) bttv_irq, NIL); |
if (0 != bttv_handle_chipset(btv)) { |
result = -EIO; |
goto fail2; |
} |
|
/* init options from insmod args */ |
btv->opt_combfilter = combfilter; |
btv->opt_lumafilter = lumafilter; |
btv->opt_automute = automute; |
btv->opt_chroma_agc = chroma_agc; |
btv->opt_adc_crush = adc_crush; |
btv->opt_vcr_hack = vcr_hack; |
|
bttv_handle_chipset(btv); |
|
init_bt848(btv); |
|
/* fill struct bttv with some useful defaults */ |
btv->init.btv = btv; |
btv->init.ov.w.width = 320; |
btv->init.ov.w.height = 240; |
btv->init.fmt = format_by_palette(VIDEO_PALETTE_RGB24); |
btv->init.width = 320; |
btv->init.height = 240; |
btv->init.lines = 16; |
btv->input = 0; |
|
/* initialize hardware */ |
if (bttv_gpio) |
bttv_gpio_tracking(btv,"pre-init"); |
|
bttv_risc_init_main(btv); |
if (!bttv_tvcards[btv->type].no_video) |
init_bt848(btv); |
|
/* gpio */ |
btwrite(0x00, BT848_GPIO_REG_INP); |
btwrite(0x00, BT848_GPIO_OUT_EN); |
if (bttv_gpio) |
bttv_gpio_tracking(btv,"init"); |
|
/* needs to be done before i2c is registered */ |
bttv_init_card1(btv); |
|
/* register i2c */ |
init_bttv_i2c(btv); |
|
/* some card-specific stuff (needs working i2c) */ |
bttv_init_card2(btv); |
|
/* register video4linux + input */ |
if (!bttv_tvcards[btv->type].no_video) { |
bttv_register_video(btv); |
#ifdef CONFIG_VIDEO_IR |
bttv_input_init(btv); |
#endif |
|
bt848_bright(btv,32768); |
bt848_contrast(btv,32768); |
bt848_hue(btv,32768); |
bt848_sat(btv,32768); |
audio_mux(btv,AUDIO_MUTE); |
set_input(btv,0); |
} |
|
/* everything is fine */ |
bttv_num++; |
|
return 0; |
|
fail2: |
free_irq(btv->dev->irq,btv); |
|
fail1: |
if (btv->bt848_mmio) |
iounmap(btv->bt848_mmio); |
release_mem_region(pci_resource_start(btv->dev,0), |
pci_resource_len(btv->dev,0)); |
pci_set_drvdata(dev,NULL); |
return result; |
} |
|
extern void i2c_init_all(void); |
|
int bttv_start(struct bttv *btv) |
static void __devexit bttv_remove(struct pci_dev *pci_dev) |
{ |
struct pci_regs *reg; |
BYTE dv, bus; |
DWORD dw; |
struct bttv *btv = pci_get_drvdata(pci_dev); |
|
printk(KERN_INFO "Initializing PCI BUS...\n"); |
|
btv->dev = vmalloc_32(sizeof(struct pci_dev)); |
if (bttv_verbose) |
printk("bttv%d: unloading\n",btv->nr); |
|
/* Scan the devices connected to the PCI bus */ |
if (pci_init() != 1) return -1; |
/* shutdown everything (DMA+IRQs) */ |
btand(~15, BT848_GPIO_DMA_CTL); |
btwrite(0, BT848_INT_MASK); |
btwrite(~0x0, BT848_INT_STAT); |
btwrite(0x0, BT848_GPIO_OUT_EN); |
if (bttv_gpio) |
bttv_gpio_tracking(btv,"cleanup"); |
|
/* tell gpio modules we are leaving ... */ |
btv->shutdown=1; |
wake_up(&btv->gpioq); |
|
/* unregister i2c_bus + input */ |
fini_bttv_i2c(btv); |
#ifdef CONFIG_VIDEO_IR |
bttv_input_fini(btv); |
#endif |
|
/* unregister video4linux */ |
bttv_unregister_video(btv); |
|
/* free allocated memory */ |
btcx_riscmem_free(btv->dev,&btv->main); |
|
/* free ressources */ |
free_irq(btv->dev->irq,btv); |
iounmap(btv->bt848_mmio); |
release_mem_region(pci_resource_start(btv->dev,0), |
pci_resource_len(btv->dev,0)); |
|
pci_set_drvdata(pci_dev, NULL); |
return; |
} |
|
static struct pci_device_id bttv_pci_tbl[] = { |
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848, |
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT849, |
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT878, |
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT879, |
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, |
{0,} |
}; |
|
MODULE_DEVICE_TABLE(pci, bttv_pci_tbl); |
|
static struct pci_driver bttv_pci_driver = { |
.name = "bttv", |
.id_table = bttv_pci_tbl, |
.probe = bttv_probe, |
.remove = __devexit_p(bttv_remove), |
}; |
|
static int bttv_init_module(void) |
{ |
int rc; |
bttv_num = 0; |
|
if (gbuffers < 2 || gbuffers > MAX_GBUFFERS) |
printk(KERN_INFO "bttv: driver version %d.%d.%d loaded\n", |
(BTTV_VERSION_CODE >> 16) & 0xff, |
(BTTV_VERSION_CODE >> 8) & 0xff, |
BTTV_VERSION_CODE & 0xff); |
if (gbuffers < 2 || gbuffers > VIDEO_MAX_FRAME) |
gbuffers = 2; |
if (gbufsize < 0 || gbufsize > BTTV_MAX_FBUF) |
gbufsize = BTTV_MAX_FBUF; |
gbufsize = (gbufsize + PAGE_SIZE - 1) & PAGE_MASK; |
if (bttv_verbose) |
printk(KERN_INFO "bttv: using %d buffers with %dk (%dk total) for capture\n", |
gbuffers,gbufsize/1024,gbuffers*gbufsize/1024); |
printk(KERN_INFO "bttv: using %d buffers with %dk (%d pages) each for capture\n", |
gbuffers, gbufsize >> 10, gbufsize >> PAGE_SHIFT); |
|
if (pci_present()) { |
|
reg = (struct pci_regs *) pci_class((0x0400) << 8,0,&bus,&dv) ; |
if (reg != NULL) { |
btv->dev->bus->number = bus; |
btv->dev->devfn = dv; |
btv->dev->base_address[0] = (reg->IoBaseAddress-0x08); |
btv->dev->irq = reg->InterruptLine; |
} else { |
return -1; |
} |
bttv_check_chipset(); |
|
printk(KERN_INFO "PCI INFO bus=%d devfn=%d irq=%d base=%08lx\n",bus,dv,btv->dev->irq,btv->dev->base_address[0]); |
rc = pci_module_init(&bttv_pci_driver); |
if (-ENODEV == rc) { |
/* plenty of people trying to use bttv for the cx2388x ... */ |
if (NULL != pci_find_device(0x14f1, 0x8800, NULL)) |
printk("bttv doesn't support your Conexant 2388x card.\n"); |
} |
return rc; |
} |
|
pcibios_read_config_dword(bus,dv,PCI_COMMAND,&dw); |
oldpci_command = dw; |
dw |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); |
pcibios_write_config_dword(bus,dv,PCI_COMMAND,dw); |
static void bttv_cleanup_module(void) |
{ |
pci_unregister_driver(&bttv_pci_driver); |
return; |
} |
|
} |
module_init(bttv_init_module); |
module_exit(bttv_cleanup_module); |
|
i2c_init_all(); |
|
bttv_check_chipset(); |
|
bttv_probe(btv); |
|
bttv_open(btv); |
|
return 0; |
|
} |
/* |
* Local variables: |
* c-basic-offset: 8 |
* End: |
*/ |