Subversion Repositories shark

Compare Revisions

Ignore whitespace Rev 427 → Rev 428

/shark/trunk/drivers/bttv/bttv-driver.c
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:
*/