Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
This commit is contained in:
Linus Torvalds
2005-04-16 15:20:36 -07:00
commit 1da177e4c3
17291 changed files with 6718755 additions and 0 deletions

View File

@@ -0,0 +1,12 @@
config VIDEO_SAA7146
tristate
select I2C
config VIDEO_SAA7146_VV
tristate
select VIDEO_BUF
select VIDEO_VIDEOBUF
select VIDEO_SAA7146
config VIDEO_VIDEOBUF
tristate

View File

@@ -0,0 +1,6 @@
saa7146-objs := saa7146_i2c.o saa7146_core.o
saa7146_vv-objs := saa7146_vv_ksyms.o saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o
obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o
obj-$(CONFIG_VIDEO_SAA7146_VV) += saa7146_vv.o
obj-$(CONFIG_VIDEO_IR) += ir-common.o

View File

@@ -0,0 +1,381 @@
/*
* $Id: ir-common.c,v 1.8 2005/02/22 12:28:40 kraxel Exp $
*
* some common structs and functions to handle infrared remotes via
* input layer ...
*
* (c) 2003 Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]
*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <media/ir-common.h>
/* -------------------------------------------------------------------------- */
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
static int repeat = 1;
module_param(repeat, int, 0444);
MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)");
static int debug = 0; /* debug level (0,1,2) */
module_param(debug, int, 0644);
#define dprintk(level, fmt, arg...) if (debug >= level) \
printk(KERN_DEBUG fmt , ## arg)
/* -------------------------------------------------------------------------- */
/* generic RC5 keytable */
/* see http://users.pandora.be/nenya/electronics/rc5/codes00.htm */
/* used by old (black) Hauppauge remotes */
IR_KEYTAB_TYPE ir_codes_rc5_tv[IR_KEYTAB_SIZE] = {
[ 0x00 ] = KEY_KP0, // 0
[ 0x01 ] = KEY_KP1, // 1
[ 0x02 ] = KEY_KP2, // 2
[ 0x03 ] = KEY_KP3, // 3
[ 0x04 ] = KEY_KP4, // 4
[ 0x05 ] = KEY_KP5, // 5
[ 0x06 ] = KEY_KP6, // 6
[ 0x07 ] = KEY_KP7, // 7
[ 0x08 ] = KEY_KP8, // 8
[ 0x09 ] = KEY_KP9, // 9
[ 0x0b ] = KEY_CHANNEL, // channel / program (japan: 11)
[ 0x0c ] = KEY_POWER, // standby
[ 0x0d ] = KEY_MUTE, // mute / demute
[ 0x0f ] = KEY_TV, // display
[ 0x10 ] = KEY_VOLUMEUP, // volume +
[ 0x11 ] = KEY_VOLUMEDOWN, // volume -
[ 0x12 ] = KEY_BRIGHTNESSUP, // brightness +
[ 0x13 ] = KEY_BRIGHTNESSDOWN, // brightness -
[ 0x1e ] = KEY_SEARCH, // search +
[ 0x20 ] = KEY_CHANNELUP, // channel / program +
[ 0x21 ] = KEY_CHANNELDOWN, // channel / program -
[ 0x22 ] = KEY_CHANNEL, // alt / channel
[ 0x23 ] = KEY_LANGUAGE, // 1st / 2nd language
[ 0x26 ] = KEY_SLEEP, // sleeptimer
[ 0x2e ] = KEY_MENU, // 2nd controls (USA: menu)
[ 0x30 ] = KEY_PAUSE, // pause
[ 0x32 ] = KEY_REWIND, // rewind
[ 0x33 ] = KEY_GOTO, // go to
[ 0x35 ] = KEY_PLAY, // play
[ 0x36 ] = KEY_STOP, // stop
[ 0x37 ] = KEY_RECORD, // recording
[ 0x3c ] = KEY_TEXT, // teletext submode (Japan: 12)
[ 0x3d ] = KEY_SUSPEND, // system standby
#if 0 /* FIXME */
[ 0x0a ] = KEY_RESERVED, // 1/2/3 digits (japan: 10)
[ 0x0e ] = KEY_RESERVED, // P.P. (personal preference)
[ 0x14 ] = KEY_RESERVED, // colour saturation +
[ 0x15 ] = KEY_RESERVED, // colour saturation -
[ 0x16 ] = KEY_RESERVED, // bass +
[ 0x17 ] = KEY_RESERVED, // bass -
[ 0x18 ] = KEY_RESERVED, // treble +
[ 0x19 ] = KEY_RESERVED, // treble -
[ 0x1a ] = KEY_RESERVED, // balance right
[ 0x1b ] = KEY_RESERVED, // balance left
[ 0x1c ] = KEY_RESERVED, // contrast +
[ 0x1d ] = KEY_RESERVED, // contrast -
[ 0x1f ] = KEY_RESERVED, // tint/hue +
[ 0x24 ] = KEY_RESERVED, // spacial stereo on/off
[ 0x25 ] = KEY_RESERVED, // mono / stereo (USA)
[ 0x27 ] = KEY_RESERVED, // tint / hue -
[ 0x28 ] = KEY_RESERVED, // RF switch/PIP select
[ 0x29 ] = KEY_RESERVED, // vote
[ 0x2a ] = KEY_RESERVED, // timed page/channel clck
[ 0x2b ] = KEY_RESERVED, // increment (USA)
[ 0x2c ] = KEY_RESERVED, // decrement (USA)
[ 0x2d ] = KEY_RESERVED, //
[ 0x2f ] = KEY_RESERVED, // PIP shift
[ 0x31 ] = KEY_RESERVED, // erase
[ 0x34 ] = KEY_RESERVED, // wind
[ 0x38 ] = KEY_RESERVED, // external 1
[ 0x39 ] = KEY_RESERVED, // external 2
[ 0x3a ] = KEY_RESERVED, // PIP display mode
[ 0x3b ] = KEY_RESERVED, // view data mode / advance
[ 0x3e ] = KEY_RESERVED, // crispener on/off
[ 0x3f ] = KEY_RESERVED, // system select
#endif
};
EXPORT_SYMBOL_GPL(ir_codes_rc5_tv);
/* Table for Leadtek Winfast Remote Controls - used by both bttv and cx88 */
IR_KEYTAB_TYPE ir_codes_winfast[IR_KEYTAB_SIZE] = {
[ 5 ] = KEY_KP1,
[ 6 ] = KEY_KP2,
[ 7 ] = KEY_KP3,
[ 9 ] = KEY_KP4,
[ 10 ] = KEY_KP5,
[ 11 ] = KEY_KP6,
[ 13 ] = KEY_KP7,
[ 14 ] = KEY_KP8,
[ 15 ] = KEY_KP9,
[ 18 ] = KEY_KP0,
[ 0 ] = KEY_POWER,
// [ 27 ] = MTS button
[ 2 ] = KEY_TUNER, // TV/FM
[ 30 ] = KEY_VIDEO,
// [ 22 ] = display button
[ 4 ] = KEY_VOLUMEUP,
[ 8 ] = KEY_VOLUMEDOWN,
[ 12 ] = KEY_CHANNELUP,
[ 16 ] = KEY_CHANNELDOWN,
[ 3 ] = KEY_ZOOM, // fullscreen
[ 31 ] = KEY_SUBTITLE, // closed caption/teletext
[ 32 ] = KEY_SLEEP,
// [ 41 ] = boss key
[ 20 ] = KEY_MUTE,
[ 43 ] = KEY_RED,
[ 44 ] = KEY_GREEN,
[ 45 ] = KEY_YELLOW,
[ 46 ] = KEY_BLUE,
[ 24 ] = KEY_KPPLUS, //fine tune +
[ 25 ] = KEY_KPMINUS, //fine tune -
// [ 42 ] = picture in picture
[ 33 ] = KEY_KPDOT,
[ 19 ] = KEY_KPENTER,
// [ 17 ] = recall
[ 34 ] = KEY_BACK,
[ 35 ] = KEY_PLAYPAUSE,
[ 36 ] = KEY_NEXT,
// [ 37 ] = time shifting
[ 38 ] = KEY_STOP,
[ 39 ] = KEY_RECORD
// [ 40 ] = snapshot
};
EXPORT_SYMBOL_GPL(ir_codes_winfast);
/* empty keytable, can be used as placeholder for not-yet created keytables */
IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = {
[ 42 ] = KEY_COFFEE,
};
EXPORT_SYMBOL_GPL(ir_codes_empty);
/* Hauppauge: the newer, gray remotes (seems there are multiple
* slightly different versions), shipped with cx88+ivtv cards.
* almost rc5 coding, but some non-standard keys */
IR_KEYTAB_TYPE ir_codes_hauppauge_new[IR_KEYTAB_SIZE] = {
[ 0x00 ] = KEY_KP0, // 0
[ 0x01 ] = KEY_KP1, // 1
[ 0x02 ] = KEY_KP2, // 2
[ 0x03 ] = KEY_KP3, // 3
[ 0x04 ] = KEY_KP4, // 4
[ 0x05 ] = KEY_KP5, // 5
[ 0x06 ] = KEY_KP6, // 6
[ 0x07 ] = KEY_KP7, // 7
[ 0x08 ] = KEY_KP8, // 8
[ 0x09 ] = KEY_KP9, // 9
[ 0x0b ] = KEY_RED, // red button
[ 0x0c ] = KEY_OPTION, // black key without text
[ 0x0d ] = KEY_MENU, // menu
[ 0x0f ] = KEY_MUTE, // mute
[ 0x10 ] = KEY_VOLUMEUP, // volume +
[ 0x11 ] = KEY_VOLUMEDOWN, // volume -
[ 0x1e ] = KEY_NEXT, // skip >|
[ 0x1f ] = KEY_EXIT, // back/exit
[ 0x20 ] = KEY_CHANNELUP, // channel / program +
[ 0x21 ] = KEY_CHANNELDOWN, // channel / program -
[ 0x22 ] = KEY_CHANNEL, // source (old black remote)
[ 0x24 ] = KEY_PREVIOUS, // replay |<
[ 0x25 ] = KEY_ENTER, // OK
[ 0x26 ] = KEY_SLEEP, // minimize (old black remote)
[ 0x29 ] = KEY_BLUE, // blue key
[ 0x2e ] = KEY_GREEN, // green button
[ 0x30 ] = KEY_PAUSE, // pause
[ 0x32 ] = KEY_REWIND, // backward <<
[ 0x34 ] = KEY_FASTFORWARD, // forward >>
[ 0x35 ] = KEY_PLAY, // play
[ 0x36 ] = KEY_STOP, // stop
[ 0x37 ] = KEY_RECORD, // recording
[ 0x38 ] = KEY_YELLOW, // yellow key
[ 0x3b ] = KEY_SELECT, // top right button
[ 0x3c ] = KEY_ZOOM, // full
[ 0x3d ] = KEY_POWER, // system power (green button)
};
EXPORT_SYMBOL(ir_codes_hauppauge_new);
/* -------------------------------------------------------------------------- */
static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir)
{
if (KEY_RESERVED == ir->keycode) {
printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n",
dev->name,ir->ir_key,ir->ir_raw,ir->keypressed);
return;
}
dprintk(1,"%s: key event code=%d down=%d\n",
dev->name,ir->keycode,ir->keypressed);
input_report_key(dev,ir->keycode,ir->keypressed);
input_sync(dev);
}
/* -------------------------------------------------------------------------- */
void ir_input_init(struct input_dev *dev, struct ir_input_state *ir,
int ir_type, IR_KEYTAB_TYPE *ir_codes)
{
int i;
ir->ir_type = ir_type;
if (ir_codes)
memcpy(ir->ir_codes, ir_codes, sizeof(ir->ir_codes));
init_input_dev(dev);
dev->keycode = ir->ir_codes;
dev->keycodesize = sizeof(IR_KEYTAB_TYPE);
dev->keycodemax = IR_KEYTAB_SIZE;
for (i = 0; i < IR_KEYTAB_SIZE; i++)
set_bit(ir->ir_codes[i], dev->keybit);
clear_bit(0, dev->keybit);
set_bit(EV_KEY, dev->evbit);
if (repeat)
set_bit(EV_REP, dev->evbit);
}
void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir)
{
if (ir->keypressed) {
ir->keypressed = 0;
ir_input_key_event(dev,ir);
}
}
void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir,
u32 ir_key, u32 ir_raw)
{
u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key);
if (ir->keypressed && ir->keycode != keycode) {
ir->keypressed = 0;
ir_input_key_event(dev,ir);
}
if (!ir->keypressed) {
ir->ir_key = ir_key;
ir->ir_raw = ir_raw;
ir->keycode = keycode;
ir->keypressed = 1;
ir_input_key_event(dev,ir);
}
#if 0
/* maybe do something like this ??? */
input_event(a, EV_IR, ir->ir_type, ir->ir_raw);
#endif
}
/* -------------------------------------------------------------------------- */
u32 ir_extract_bits(u32 data, u32 mask)
{
int mbit, vbit;
u32 value;
value = 0;
vbit = 0;
for (mbit = 0; mbit < 32; mbit++) {
if (!(mask & ((u32)1 << mbit)))
continue;
if (data & ((u32)1 << mbit))
value |= (1 << vbit);
vbit++;
}
return value;
}
static int inline getbit(u32 *samples, int bit)
{
return (samples[bit/32] & (1 << (31-(bit%32)))) ? 1 : 0;
}
/* sump raw samples for visual debugging ;) */
int ir_dump_samples(u32 *samples, int count)
{
int i, bit, start;
printk(KERN_DEBUG "ir samples: ");
start = 0;
for (i = 0; i < count * 32; i++) {
bit = getbit(samples,i);
if (bit)
start = 1;
if (0 == start)
continue;
printk("%s", bit ? "#" : "_");
}
printk("\n");
return 0;
}
/* decode raw samples, biphase coding, used by rc5 for example */
int ir_decode_biphase(u32 *samples, int count, int low, int high)
{
int i,last,bit,len,flips;
u32 value;
/* find start bit (1) */
for (i = 0; i < 32; i++) {
bit = getbit(samples,i);
if (bit)
break;
}
/* go decoding */
len = 0;
flips = 0;
value = 1;
for (; i < count * 32; i++) {
if (len > high)
break;
if (flips > 1)
break;
last = bit;
bit = getbit(samples,i);
if (last == bit) {
len++;
continue;
}
if (len < low) {
len++;
flips++;
continue;
}
value <<= 1;
value |= bit;
flips = 0;
len = 1;
}
return value;
}
EXPORT_SYMBOL_GPL(ir_input_init);
EXPORT_SYMBOL_GPL(ir_input_nokey);
EXPORT_SYMBOL_GPL(ir_input_keydown);
EXPORT_SYMBOL_GPL(ir_extract_bits);
EXPORT_SYMBOL_GPL(ir_dump_samples);
EXPORT_SYMBOL_GPL(ir_decode_biphase);
/*
* Local variables:
* c-basic-offset: 8
* End:
*/

View File

@@ -0,0 +1,547 @@
/*
saa7146.o - driver for generic saa7146-based hardware
Copyright (C) 1998-2003 Michael Hunold <michael@mihu.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <media/saa7146.h>
LIST_HEAD(saa7146_devices);
DECLARE_MUTEX(saa7146_devices_lock);
static int saa7146_num = 0;
unsigned int saa7146_debug = 0;
module_param(saa7146_debug, int, 0644);
MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
#if 0
static void dump_registers(struct saa7146_dev* dev)
{
int i = 0;
INFO((" @ %li jiffies:\n",jiffies));
for(i = 0; i <= 0x148; i+=4) {
printk("0x%03x: 0x%08x\n",i,saa7146_read(dev,i));
}
}
#endif
/****************************************************************************
* gpio and debi helper functions
****************************************************************************/
void saa7146_setgpio(struct saa7146_dev *dev, int port, u32 data)
{
u32 value = 0;
BUG_ON(port > 3);
value = saa7146_read(dev, GPIO_CTRL);
value &= ~(0xff << (8*port));
value |= (data << (8*port));
saa7146_write(dev, GPIO_CTRL, value);
}
/* This DEBI code is based on the saa7146 Stradis driver by Nathan Laredo */
int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
{
unsigned long start;
/* wait for registers to be programmed */
start = jiffies;
while (1) {
if (saa7146_read(dev, MC2) & 2)
break;
if (time_after(jiffies, start + HZ/20)) {
DEB_S(("timed out while waiting for registers getting programmed\n"));
return -ETIMEDOUT;
}
if (nobusyloop)
msleep(1);
}
/* wait for transfer to complete */
start = jiffies;
while (1) {
if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
break;
saa7146_read(dev, MC2);
if (time_after(jiffies, start + HZ/4)) {
DEB_S(("timed out while waiting for transfer completion\n"));
return -ETIMEDOUT;
}
if (nobusyloop)
msleep(1);
}
return 0;
}
/****************************************************************************
* general helper functions
****************************************************************************/
/* this is videobuf_vmalloc_to_sg() from video-buf.c
make sure virt has been allocated with vmalloc_32(), otherwise the BUG()
may be triggered on highmem machines */
static struct scatterlist* vmalloc_to_sg(unsigned char *virt, int nr_pages)
{
struct scatterlist *sglist;
struct page *pg;
int i;
sglist = kmalloc(sizeof(struct scatterlist)*nr_pages, GFP_KERNEL);
if (NULL == sglist)
return NULL;
memset(sglist,0,sizeof(struct scatterlist)*nr_pages);
for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
pg = vmalloc_to_page(virt);
if (NULL == pg)
goto err;
if (PageHighMem(pg))
BUG();
sglist[i].page = pg;
sglist[i].length = PAGE_SIZE;
}
return sglist;
err:
kfree(sglist);
return NULL;
}
/********************************************************************************/
/* common page table functions */
char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
{
int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
char *mem = vmalloc_32(length);
int slen = 0;
if (NULL == mem) {
return NULL;
}
if (!(pt->slist = vmalloc_to_sg(mem, pages))) {
vfree(mem);
return NULL;
}
if (saa7146_pgtable_alloc(pci, pt)) {
kfree(pt->slist);
pt->slist = NULL;
vfree(mem);
return NULL;
}
slen = pci_map_sg(pci,pt->slist,pages,PCI_DMA_FROMDEVICE);
if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) {
return NULL;
}
return mem;
}
void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
if (NULL == pt->cpu)
return;
pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
pt->cpu = NULL;
if (NULL != pt->slist) {
kfree(pt->slist);
pt->slist = NULL;
}
}
int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
u32 *cpu;
dma_addr_t dma_addr;
cpu = pci_alloc_consistent(pci, PAGE_SIZE, &dma_addr);
if (NULL == cpu) {
return -ENOMEM;
}
pt->size = PAGE_SIZE;
pt->cpu = cpu;
pt->dma = dma_addr;
return 0;
}
int saa7146_pgtable_build_single(struct pci_dev *pci, struct saa7146_pgtable *pt,
struct scatterlist *list, int sglen )
{
u32 *ptr, fill;
int nr_pages = 0;
int i,p;
BUG_ON(0 == sglen);
BUG_ON(list->offset > PAGE_SIZE);
/* if we have a user buffer, the first page may not be
aligned to a page boundary. */
pt->offset = list->offset;
ptr = pt->cpu;
for (i = 0; i < sglen; i++, list++) {
/*
printk("i:%d, adr:0x%08x, len:%d, offset:%d\n", i,sg_dma_address(list), sg_dma_len(list), list->offset);
*/
for (p = 0; p * 4096 < list->length; p++, ptr++) {
*ptr = cpu_to_le32(sg_dma_address(list) + p * 4096);
nr_pages++;
}
}
/* safety; fill the page table up with the last valid page */
fill = *(ptr-1);
for(i=nr_pages;i<1024;i++) {
*ptr++ = fill;
}
/*
ptr = pt->cpu;
printk("offset: %d\n",pt->offset);
for(i=0;i<5;i++) {
printk("ptr1 %d: 0x%08x\n",i,ptr[i]);
}
*/
return 0;
}
/********************************************************************************/
/* interrupt handler */
static irqreturn_t interrupt_hw(int irq, void *dev_id, struct pt_regs *regs)
{
struct saa7146_dev *dev = dev_id;
u32 isr = 0;
/* read out the interrupt status register */
isr = saa7146_read(dev, ISR);
/* is this our interrupt? */
if ( 0 == isr ) {
/* nope, some other device */
return IRQ_NONE;
}
saa7146_write(dev, ISR, isr);
if( 0 != (dev->ext)) {
if( 0 != (dev->ext->irq_mask & isr )) {
if( 0 != dev->ext->irq_func ) {
dev->ext->irq_func(dev, &isr);
}
isr &= ~dev->ext->irq_mask;
}
}
if (0 != (isr & (MASK_27))) {
DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
if( 0 != dev->vv_data && 0 != dev->vv_callback) {
dev->vv_callback(dev,isr);
}
isr &= ~MASK_27;
}
if (0 != (isr & (MASK_28))) {
if( 0 != dev->vv_data && 0 != dev->vv_callback) {
dev->vv_callback(dev,isr);
}
isr &= ~MASK_28;
}
if (0 != (isr & (MASK_16|MASK_17))) {
u32 status = saa7146_read(dev, I2C_STATUS);
if( (0x3 == (status & 0x3)) || (0 == (status & 0x1)) ) {
SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
/* only wake up if we expect something */
if( 0 != dev->i2c_op ) {
u32 psr = (saa7146_read(dev, PSR) >> 16) & 0x2;
u32 ssr = (saa7146_read(dev, SSR) >> 17) & 0x1f;
DEB_I2C(("irq: i2c, status: 0x%08x, psr:0x%02x, ssr:0x%02x).\n",status,psr,ssr));
dev->i2c_op = 0;
wake_up(&dev->i2c_wq);
} else {
DEB_I2C(("unexpected irq: i2c, status: 0x%08x, isr %#x\n",status, isr));
}
} else {
DEB_I2C(("unhandled irq: i2c, status: 0x%08x, isr %#x\n",status, isr));
}
isr &= ~(MASK_16|MASK_17);
}
if( 0 != isr ) {
ERR(("warning: interrupt enabled, but not handled properly.(0x%08x)\n",isr));
ERR(("disabling interrupt source(s)!\n"));
SAA7146_IER_DISABLE(dev,isr);
}
return IRQ_HANDLED;
}
/*********************************************************************************/
/* configuration-functions */
static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent)
{
struct saa7146_pci_extension_data *pci_ext = (struct saa7146_pci_extension_data *)ent->driver_data;
struct saa7146_extension *ext = pci_ext->ext;
struct saa7146_dev *dev;
int err = -ENOMEM;
dev = kmalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
if (!dev) {
ERR(("out of memory.\n"));
goto out;
}
/* clear out mem for sure */
memset(dev, 0x0, sizeof(struct saa7146_dev));
DEB_EE(("pci:%p\n",pci));
err = pci_enable_device(pci);
if (err < 0) {
ERR(("pci_enable_device() failed.\n"));
goto err_free;
}
/* enable bus-mastering */
pci_set_master(pci);
dev->pci = pci;
/* get chip-revision; this is needed to enable bug-fixes */
err = pci_read_config_dword(pci, PCI_CLASS_REVISION, &dev->revision);
if (err < 0) {
ERR(("pci_read_config_dword() failed.\n"));
goto err_disable;
}
dev->revision &= 0xf;
/* remap the memory from virtual to physical adress */
err = pci_request_region(pci, 0, "saa7146");
if (err < 0)
goto err_disable;
dev->mem = ioremap(pci_resource_start(pci, 0),
pci_resource_len(pci, 0));
if (!dev->mem) {
ERR(("ioremap() failed.\n"));
err = -ENODEV;
goto err_release;
}
/* we don't do a master reset here anymore, it screws up
some boards that don't have an i2c-eeprom for configuration
values */
/*
saa7146_write(dev, MC1, MASK_31);
*/
/* disable all irqs */
saa7146_write(dev, IER, 0);
/* shut down all dma transfers and rps tasks */
saa7146_write(dev, MC1, 0x30ff0000);
/* clear out any rps-signals pending */
saa7146_write(dev, MC2, 0xf8000000);
/* request an interrupt for the saa7146 */
err = request_irq(pci->irq, interrupt_hw, SA_SHIRQ | SA_INTERRUPT,
dev->name, dev);
if (err < 0) {
ERR(("request_irq() failed.\n"));
goto err_unmap;
}
err = -ENOMEM;
/* get memory for various stuff */
dev->d_rps0.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
&dev->d_rps0.dma_handle);
if (!dev->d_rps0.cpu_addr)
goto err_free_irq;
memset(dev->d_rps0.cpu_addr, 0x0, SAA7146_RPS_MEM);
dev->d_rps1.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
&dev->d_rps1.dma_handle);
if (!dev->d_rps1.cpu_addr)
goto err_free_rps0;
memset(dev->d_rps1.cpu_addr, 0x0, SAA7146_RPS_MEM);
dev->d_i2c.cpu_addr = pci_alloc_consistent(pci, SAA7146_RPS_MEM,
&dev->d_i2c.dma_handle);
if (!dev->d_i2c.cpu_addr)
goto err_free_rps1;
memset(dev->d_i2c.cpu_addr, 0x0, SAA7146_RPS_MEM);
/* the rest + print status message */
/* create a nice device name */
sprintf(dev->name, "saa7146 (%d)", saa7146_num);
INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device));
dev->ext = ext;
pci_set_drvdata(pci, dev);
init_MUTEX(&dev->lock);
spin_lock_init(&dev->int_slock);
spin_lock_init(&dev->slock);
init_MUTEX(&dev->i2c_lock);
dev->module = THIS_MODULE;
init_waitqueue_head(&dev->i2c_wq);
/* set some sane pci arbitrition values */
saa7146_write(dev, PCI_BT_V1, 0x1c00101f);
/* TODO: use the status code of the callback */
err = -ENODEV;
if (ext->probe && ext->probe(dev)) {
DEB_D(("ext->probe() failed for %p. skipping device.\n",dev));
goto err_free_i2c;
}
if (ext->attach(dev, pci_ext)) {
DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
goto err_unprobe;
}
INIT_LIST_HEAD(&dev->item);
list_add_tail(&dev->item,&saa7146_devices);
saa7146_num++;
err = 0;
out:
return err;
err_unprobe:
pci_set_drvdata(pci, NULL);
err_free_i2c:
pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
dev->d_i2c.dma_handle);
err_free_rps1:
pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps1.cpu_addr,
dev->d_rps1.dma_handle);
err_free_rps0:
pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_rps0.cpu_addr,
dev->d_rps0.dma_handle);
err_free_irq:
free_irq(pci->irq, (void *)dev);
err_unmap:
iounmap(dev->mem);
err_release:
pci_release_region(pci, 0);
err_disable:
pci_disable_device(pci);
err_free:
kfree(dev);
goto out;
}
static void saa7146_remove_one(struct pci_dev *pdev)
{
struct saa7146_dev* dev = pci_get_drvdata(pdev);
struct {
void *addr;
dma_addr_t dma;
} dev_map[] = {
{ dev->d_i2c.cpu_addr, dev->d_i2c.dma_handle },
{ dev->d_rps1.cpu_addr, dev->d_rps1.dma_handle },
{ dev->d_rps0.cpu_addr, dev->d_rps0.dma_handle },
{ NULL, 0 }
}, *p;
DEB_EE(("dev:%p\n",dev));
dev->ext->detach(dev);
/* shut down all video dma transfers */
saa7146_write(dev, MC1, 0x00ff0000);
/* disable all irqs, release irq-routine */
saa7146_write(dev, IER, 0);
free_irq(pdev->irq, dev);
for (p = dev_map; p->addr; p++)
pci_free_consistent(pdev, SAA7146_RPS_MEM, p->addr, p->dma);
iounmap(dev->mem);
pci_release_region(pdev, 0);
list_del(&dev->item);
pci_disable_device(pdev);
kfree(dev);
saa7146_num--;
}
/*********************************************************************************/
/* extension handling functions */
int saa7146_register_extension(struct saa7146_extension* ext)
{
DEB_EE(("ext:%p\n",ext));
ext->driver.name = ext->name;
ext->driver.id_table = ext->pci_tbl;
ext->driver.probe = saa7146_init_one;
ext->driver.remove = saa7146_remove_one;
printk("saa7146: register extension '%s'.\n",ext->name);
return pci_module_init(&ext->driver);
}
int saa7146_unregister_extension(struct saa7146_extension* ext)
{
DEB_EE(("ext:%p\n",ext));
printk("saa7146: unregister extension '%s'.\n",ext->name);
pci_unregister_driver(&ext->driver);
return 0;
}
EXPORT_SYMBOL_GPL(saa7146_register_extension);
EXPORT_SYMBOL_GPL(saa7146_unregister_extension);
/* misc functions used by extension modules */
EXPORT_SYMBOL_GPL(saa7146_pgtable_alloc);
EXPORT_SYMBOL_GPL(saa7146_pgtable_free);
EXPORT_SYMBOL_GPL(saa7146_pgtable_build_single);
EXPORT_SYMBOL_GPL(saa7146_vmalloc_build_pgtable);
EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
EXPORT_SYMBOL_GPL(saa7146_setgpio);
EXPORT_SYMBOL_GPL(saa7146_i2c_transfer);
EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
EXPORT_SYMBOL_GPL(saa7146_debug);
EXPORT_SYMBOL_GPL(saa7146_devices);
EXPORT_SYMBOL_GPL(saa7146_devices_lock);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,564 @@
#include <media/saa7146_vv.h>
#include <linux/version.h>
#define BOARD_CAN_DO_VBI(dev) (dev->revision != 0 && dev->vv_data->vbi_minor != -1)
/****************************************************************************/
/* resource management functions, shamelessly stolen from saa7134 driver */
int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
if (fh->resources & bit) {
DEB_D(("already allocated! want: 0x%02x, cur:0x%02x\n",bit,vv->resources));
/* have it already allocated */
return 1;
}
/* is it free? */
down(&dev->lock);
if (vv->resources & bit) {
DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
/* no, someone else uses it */
up(&dev->lock);
return 0;
}
/* it's free, grab it */
fh->resources |= bit;
vv->resources |= bit;
DEB_D(("res: get 0x%02x, cur:0x%02x\n",bit,vv->resources));
up(&dev->lock);
return 1;
}
void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
if ((fh->resources & bits) != bits)
BUG();
down(&dev->lock);
fh->resources &= ~bits;
vv->resources &= ~bits;
DEB_D(("res: put 0x%02x, cur:0x%02x\n",bits,vv->resources));
up(&dev->lock);
}
/********************************************************************************/
/* common dma functions */
void saa7146_dma_free(struct saa7146_dev *dev,struct saa7146_buf *buf)
{
DEB_EE(("dev:%p, buf:%p\n",dev,buf));
if (in_interrupt())
BUG();
videobuf_waiton(&buf->vb,0,0);
videobuf_dma_pci_unmap(dev->pci, &buf->vb.dma);
videobuf_dma_free(&buf->vb.dma);
buf->vb.state = STATE_NEEDS_INIT;
}
/********************************************************************************/
/* common buffer functions */
int saa7146_buffer_queue(struct saa7146_dev *dev,
struct saa7146_dmaqueue *q,
struct saa7146_buf *buf)
{
assert_spin_locked(&dev->slock);
DEB_EE(("dev:%p, dmaq:%p, buf:%p\n", dev, q, buf));
BUG_ON(!q);
if (NULL == q->curr) {
q->curr = buf;
DEB_D(("immediately activating buffer %p\n", buf));
buf->activate(dev,buf,NULL);
} else {
list_add_tail(&buf->vb.queue,&q->queue);
buf->vb.state = STATE_QUEUED;
DEB_D(("adding buffer %p to queue. (active buffer present)\n", buf));
}
return 0;
}
void saa7146_buffer_finish(struct saa7146_dev *dev,
struct saa7146_dmaqueue *q,
int state)
{
assert_spin_locked(&dev->slock);
DEB_EE(("dev:%p, dmaq:%p, state:%d\n", dev, q, state));
DEB_EE(("q->curr:%p\n",q->curr));
BUG_ON(!q->curr);
/* finish current buffer */
if (NULL == q->curr) {
DEB_D(("aiii. no current buffer\n"));
return;
}
q->curr->vb.state = state;
do_gettimeofday(&q->curr->vb.ts);
wake_up(&q->curr->vb.done);
q->curr = NULL;
}
void saa7146_buffer_next(struct saa7146_dev *dev,
struct saa7146_dmaqueue *q, int vbi)
{
struct saa7146_buf *buf,*next = NULL;
BUG_ON(!q);
DEB_INT(("dev:%p, dmaq:%p, vbi:%d\n", dev, q, vbi));
assert_spin_locked(&dev->slock);
if (!list_empty(&q->queue)) {
/* activate next one from queue */
buf = list_entry(q->queue.next,struct saa7146_buf,vb.queue);
list_del(&buf->vb.queue);
if (!list_empty(&q->queue))
next = list_entry(q->queue.next,struct saa7146_buf, vb.queue);
q->curr = buf;
DEB_INT(("next buffer: buf:%p, prev:%p, next:%p\n", buf, q->queue.prev,q->queue.next));
buf->activate(dev,buf,next);
} else {
DEB_INT(("no next buffer. stopping.\n"));
if( 0 != vbi ) {
/* turn off video-dma3 */
saa7146_write(dev,MC1, MASK_20);
} else {
/* nothing to do -- just prevent next video-dma1 transfer
by lowering the protection address */
// fixme: fix this for vflip != 0
saa7146_write(dev, PROT_ADDR1, 0);
saa7146_write(dev, MC2, (MASK_02|MASK_18));
/* write the address of the rps-program */
saa7146_write(dev, RPS_ADDR0, dev->d_rps0.dma_handle);
/* turn on rps */
saa7146_write(dev, MC1, (MASK_12 | MASK_28));
/*
printk("vdma%d.base_even: 0x%08x\n", 1,saa7146_read(dev,BASE_EVEN1));
printk("vdma%d.base_odd: 0x%08x\n", 1,saa7146_read(dev,BASE_ODD1));
printk("vdma%d.prot_addr: 0x%08x\n", 1,saa7146_read(dev,PROT_ADDR1));
printk("vdma%d.base_page: 0x%08x\n", 1,saa7146_read(dev,BASE_PAGE1));
printk("vdma%d.pitch: 0x%08x\n", 1,saa7146_read(dev,PITCH1));
printk("vdma%d.num_line_byte: 0x%08x\n", 1,saa7146_read(dev,NUM_LINE_BYTE1));
*/
}
del_timer(&q->timeout);
}
}
void saa7146_buffer_timeout(unsigned long data)
{
struct saa7146_dmaqueue *q = (struct saa7146_dmaqueue*)data;
struct saa7146_dev *dev = q->dev;
unsigned long flags;
DEB_EE(("dev:%p, dmaq:%p\n", dev, q));
spin_lock_irqsave(&dev->slock,flags);
if (q->curr) {
DEB_D(("timeout on %p\n", q->curr));
saa7146_buffer_finish(dev,q,STATE_ERROR);
}
/* we don't restart the transfer here like other drivers do. when
a streaming capture is disabled, the timeout function will be
called for the current buffer. if we activate the next buffer now,
we mess up our capture logic. if a timeout occurs on another buffer,
then something is seriously broken before, so no need to buffer the
next capture IMHO... */
/*
saa7146_buffer_next(dev,q);
*/
spin_unlock_irqrestore(&dev->slock,flags);
}
/********************************************************************************/
/* file operations */
static int fops_open(struct inode *inode, struct file *file)
{
unsigned int minor = iminor(inode);
struct saa7146_dev *h = NULL, *dev = NULL;
struct list_head *list;
struct saa7146_fh *fh = NULL;
int result = 0;
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
DEB_EE(("inode:%p, file:%p, minor:%d\n",inode,file,minor));
if (down_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
list_for_each(list,&saa7146_devices) {
h = list_entry(list, struct saa7146_dev, item);
if( NULL == h->vv_data ) {
DEB_D(("device %p has not registered video devices.\n",h));
continue;
}
DEB_D(("trying: %p @ major %d,%d\n",h,h->vv_data->video_minor,h->vv_data->vbi_minor));
if (h->vv_data->video_minor == minor) {
dev = h;
}
if (h->vv_data->vbi_minor == minor) {
type = V4L2_BUF_TYPE_VBI_CAPTURE;
dev = h;
}
}
if (NULL == dev) {
DEB_S(("no such video device.\n"));
result = -ENODEV;
goto out;
}
DEB_D(("using: %p\n",dev));
/* check if an extension is registered */
if( NULL == dev->ext ) {
DEB_S(("no extension registered for this device.\n"));
result = -ENODEV;
goto out;
}
/* allocate per open data */
fh = kmalloc(sizeof(*fh),GFP_KERNEL);
if (NULL == fh) {
DEB_S(("cannot allocate memory for per open data.\n"));
result = -ENOMEM;
goto out;
}
memset(fh,0,sizeof(*fh));
file->private_data = fh;
fh->dev = dev;
fh->type = type;
if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
DEB_S(("initializing vbi...\n"));
result = saa7146_vbi_uops.open(dev,file);
} else {
DEB_S(("initializing video...\n"));
result = saa7146_video_uops.open(dev,file);
}
if (0 != result) {
goto out;
}
if( 0 == try_module_get(dev->ext->module)) {
result = -EINVAL;
goto out;
}
result = 0;
out:
if( fh != 0 && result != 0 ) {
kfree(fh);
file->private_data = NULL;
}
up(&saa7146_devices_lock);
return result;
}
static int fops_release(struct inode *inode, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
DEB_EE(("inode:%p, file:%p\n",inode,file));
if (down_interruptible(&saa7146_devices_lock))
return -ERESTARTSYS;
if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
saa7146_vbi_uops.release(dev,file);
} else {
saa7146_video_uops.release(dev,file);
}
module_put(dev->ext->module);
file->private_data = NULL;
kfree(fh);
up(&saa7146_devices_lock);
return 0;
}
int saa7146_video_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg);
static int fops_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
/*
DEB_EE(("inode:%p, file:%p, cmd:%d, arg:%li\n",inode, file, cmd, arg));
*/
return video_usercopy(inode, file, cmd, arg, saa7146_video_do_ioctl);
}
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
{
struct saa7146_fh *fh = file->private_data;
struct videobuf_queue *q;
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",file, vma));
q = &fh->video_q;
break;
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",file, vma));
q = &fh->vbi_q;
break;
}
default:
BUG();
return 0;
}
return videobuf_mmap_mapper(q,vma);
}
static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
{
struct saa7146_fh *fh = file->private_data;
struct videobuf_buffer *buf = NULL;
struct videobuf_queue *q;
DEB_EE(("file:%p, poll:%p\n",file, wait));
if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) {
if( 0 == fh->vbi_q.streaming )
return videobuf_poll_stream(file, &fh->vbi_q, wait);
q = &fh->vbi_q;
} else {
DEB_D(("using video queue.\n"));
q = &fh->video_q;
}
if (!list_empty(&q->stream))
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
if (!buf) {
DEB_D(("buf == NULL!\n"));
return POLLERR;
}
poll_wait(file, &buf->done, wait);
if (buf->state == STATE_DONE || buf->state == STATE_ERROR) {
DEB_D(("poll succeeded!\n"));
return POLLIN|POLLRDNORM;
}
DEB_D(("nothing to poll for, buf->state:%d\n",buf->state));
return 0;
}
static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct saa7146_fh *fh = file->private_data;
switch (fh->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
// DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count));
return saa7146_video_uops.read(file,data,count,ppos);
}
case V4L2_BUF_TYPE_VBI_CAPTURE: {
// DEB_EE(("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count));
return saa7146_vbi_uops.read(file,data,count,ppos);
}
break;
default:
BUG();
return 0;
}
}
static struct file_operations video_fops =
{
.owner = THIS_MODULE,
.open = fops_open,
.release = fops_release,
.read = fops_read,
.poll = fops_poll,
.mmap = fops_mmap,
.ioctl = fops_ioctl,
.llseek = no_llseek,
};
void vv_callback(struct saa7146_dev *dev, unsigned long status)
{
u32 isr = status;
DEB_INT(("dev:%p, isr:0x%08x\n",dev,(u32)status));
if (0 != (isr & (MASK_27))) {
DEB_INT(("irq: RPS0 (0x%08x).\n",isr));
saa7146_video_uops.irq_done(dev,isr);
}
if (0 != (isr & (MASK_28))) {
u32 mc2 = saa7146_read(dev, MC2);
if( 0 != (mc2 & MASK_15)) {
DEB_INT(("irq: RPS1 vbi workaround (0x%08x).\n",isr));
wake_up(&dev->vv_data->vbi_wq);
saa7146_write(dev,MC2, MASK_31);
return;
}
DEB_INT(("irq: RPS1 (0x%08x).\n",isr));
saa7146_vbi_uops.irq_done(dev,isr);
}
}
static struct video_device device_template =
{
.hardware = VID_HARDWARE_SAA7146,
.fops = &video_fops,
.minor = -1,
};
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
{
struct saa7146_vv *vv = kmalloc (sizeof(struct saa7146_vv),GFP_KERNEL);
if( NULL == vv ) {
ERR(("out of memory. aborting.\n"));
return -1;
}
memset(vv, 0x0, sizeof(*vv));
DEB_EE(("dev:%p\n",dev));
/* set default values for video parts of the saa7146 */
saa7146_write(dev, BCS_CTRL, 0x80400040);
/* enable video-port pins */
saa7146_write(dev, MC1, (MASK_10 | MASK_26));
/* save per-device extension data (one extension can
handle different devices that might need different
configuration data) */
dev->ext_vv_data = ext_vv;
vv->video_minor = -1;
vv->vbi_minor = -1;
vv->d_clipping.cpu_addr = pci_alloc_consistent(dev->pci, SAA7146_CLIPPING_MEM, &vv->d_clipping.dma_handle);
if( NULL == vv->d_clipping.cpu_addr ) {
ERR(("out of memory. aborting.\n"));
kfree(vv);
return -1;
}
memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM);
saa7146_video_uops.init(dev,vv);
saa7146_vbi_uops.init(dev,vv);
dev->vv_data = vv;
dev->vv_callback = &vv_callback;
return 0;
}
int saa7146_vv_release(struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
DEB_EE(("dev:%p\n",dev));
pci_free_consistent(dev->pci, SAA7146_RPS_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
kfree(vv);
dev->vv_data = NULL;
dev->vv_callback = NULL;
return 0;
}
int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
char *name, int type)
{
struct saa7146_vv *vv = dev->vv_data;
struct video_device *vfd;
DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
// released by vfd->release
vfd = video_device_alloc();
if (vfd == NULL)
return -ENOMEM;
memcpy(vfd, &device_template, sizeof(struct video_device));
strlcpy(vfd->name, name, sizeof(vfd->name));
vfd->release = video_device_release;
vfd->priv = dev;
// fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
if (video_register_device(vfd, type, -1) < 0) {
ERR(("cannot register v4l2 device. skipping.\n"));
return -1;
}
if( VFL_TYPE_GRABBER == type ) {
vv->video_minor = vfd->minor;
INFO(("%s: registered device video%d [v4l2]\n",
dev->name, vfd->minor & 0x1f));
} else {
vv->vbi_minor = vfd->minor;
INFO(("%s: registered device vbi%d [v4l2]\n",
dev->name, vfd->minor & 0x1f));
}
*vid = vfd;
return 0;
}
int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
DEB_EE(("dev:%p\n",dev));
if( VFL_TYPE_GRABBER == (*vid)->type ) {
vv->video_minor = -1;
} else {
vv->vbi_minor = -1;
}
video_unregister_device(*vid);
*vid = NULL;
return 0;
}
static int __init saa7146_vv_init_module(void)
{
return 0;
}
static void __exit saa7146_vv_cleanup_module(void)
{
}
module_init(saa7146_vv_init_module);
module_exit(saa7146_vv_cleanup_module);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,421 @@
#include <linux/version.h>
#include <media/saa7146_vv.h>
static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
//fm DEB_I2C(("'%s'.\n", adapter->name));
return I2C_FUNC_I2C
| I2C_FUNC_SMBUS_QUICK
| I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE
| I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA;
}
/* this function returns the status-register of our i2c-device */
static inline u32 saa7146_i2c_status(struct saa7146_dev *dev)
{
u32 iicsta = saa7146_read(dev, I2C_STATUS);
/*
DEB_I2C(("status: 0x%08x\n",iicsta));
*/
return iicsta;
}
/* this function runs through the i2c-messages and prepares the data to be
sent through the saa7146. have a look at the specifications p. 122 ff
to understand this. it returns the number of u32s to send, or -1
in case of an error. */
static int saa7146_i2c_msg_prepare(const struct i2c_msg *m, int num, u32 *op)
{
int h1, h2;
int i, j, addr;
int mem = 0, op_count = 0;
/* first determine size of needed memory */
for(i = 0; i < num; i++) {
mem += m[i].len + 1;
}
/* worst case: we need one u32 for three bytes to be send
plus one extra byte to address the device */
mem = 1 + ((mem-1) / 3);
/* we assume that op points to a memory of at least SAA7146_I2C_MEM bytes
size. if we exceed this limit... */
if ( (4*mem) > SAA7146_I2C_MEM ) {
//fm DEB_I2C(("cannot prepare i2c-message.\n"));
return -ENOMEM;
}
/* be careful: clear out the i2c-mem first */
memset(op,0,sizeof(u32)*mem);
/* loop through all messages */
for(i = 0; i < num; i++) {
/* insert the address of the i2c-slave.
note: we get 7 bit i2c-addresses,
so we have to perform a translation */
addr = (m[i].addr*2) + ( (0 != (m[i].flags & I2C_M_RD)) ? 1 : 0);
h1 = op_count/3; h2 = op_count%3;
op[h1] |= ( (u8)addr << ((3-h2)*8));
op[h1] |= (SAA7146_I2C_START << ((3-h2)*2));
op_count++;
/* loop through all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* insert the data bytes */
h1 = op_count/3; h2 = op_count%3;
op[h1] |= ( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
op[h1] |= ( SAA7146_I2C_CONT << ((3-h2)*2));
op_count++;
}
}
/* have a look at the last byte inserted:
if it was: ...CONT change it to ...STOP */
h1 = (op_count-1)/3; h2 = (op_count-1)%3;
if ( SAA7146_I2C_CONT == (0x3 & (op[h1] >> ((3-h2)*2))) ) {
op[h1] &= ~(0x2 << ((3-h2)*2));
op[h1] |= (SAA7146_I2C_STOP << ((3-h2)*2));
}
/* return the number of u32s to send */
return mem;
}
/* this functions loops through all i2c-messages. normally, it should determine
which bytes were read through the adapter and write them back to the corresponding
i2c-message. but instead, we simply write back all bytes.
fixme: this could be improved. */
static int saa7146_i2c_msg_cleanup(const struct i2c_msg *m, int num, u32 *op)
{
int i, j;
int op_count = 0;
/* loop through all messages */
for(i = 0; i < num; i++) {
op_count++;
/* loop throgh all bytes of message i */
for(j = 0; j < m[i].len; j++) {
/* write back all bytes that could have been read */
m[i].buf[j] = (op[op_count/3] >> ((3-(op_count%3))*8));
op_count++;
}
}
return 0;
}
/* this functions resets the i2c-device and returns 0 if everything was fine, otherwise -1 */
static int saa7146_i2c_reset(struct saa7146_dev *dev)
{
/* get current status */
u32 status = saa7146_i2c_status(dev);
/* clear registers for sure */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, 0);
/* check if any operation is still in progress */
if ( 0 != ( status & SAA7146_I2C_BUSY) ) {
/* yes, kill ongoing operation */
DEB_I2C(("busy_state detected.\n"));
/* set "ABORT-OPERATION"-bit (bit 7)*/
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
/* clear all error-bits pending; this is needed because p.123, note 1 */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
}
/* check if any error is (still) present. (this can be necessary because p.123, note 1) */
status = saa7146_i2c_status(dev);
if ( dev->i2c_bitrate != status ) {
DEB_I2C(("error_state detected. status:0x%08x\n",status));
/* Repeat the abort operation. This seems to be necessary
after serious protocol errors caused by e.g. the SAA7740 */
saa7146_write(dev, I2C_STATUS, (dev->i2c_bitrate | MASK_07));
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
/* clear all error-bits pending */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
/* the data sheet says it might be necessary to clear the status
twice after an abort */
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
msleep(SAA7146_I2C_DELAY);
}
/* if any error is still present, a fatal error has occured ... */
status = saa7146_i2c_status(dev);
if ( dev->i2c_bitrate != status ) {
DEB_I2C(("fatal error. status:0x%08x\n",status));
return -1;
}
return 0;
}
/* this functions writes out the data-byte 'dword' to the i2c-device.
it returns 0 if ok, -1 if the transfer failed, -2 if the transfer
failed badly (e.g. address error) */
static int saa7146_i2c_writeout(struct saa7146_dev *dev, u32* dword, int short_delay)
{
u32 status = 0, mc2 = 0;
int trial = 0;
unsigned long timeout;
/* write out i2c-command */
DEB_I2C(("before: 0x%08x (status: 0x%08x), %d\n",*dword,saa7146_read(dev, I2C_STATUS), dev->i2c_op));
if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, *dword);
dev->i2c_op = 1;
SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
wait_event_interruptible(dev->i2c_wq, dev->i2c_op == 0);
if (signal_pending (current)) {
/* a signal arrived */
return -ERESTARTSYS;
}
status = saa7146_read(dev, I2C_STATUS);
} else {
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, *dword);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
/* do not poll for i2c-status before upload is complete */
timeout = jiffies + HZ/100 + 1; /* 10ms */
while(1) {
mc2 = (saa7146_read(dev, MC2) & 0x1);
if( 0 != mc2 ) {
break;
}
if (time_after(jiffies,timeout)) {
printk(KERN_WARNING "saa7146_i2c_writeout: timed out waiting for MC2\n");
return -EIO;
}
}
/* wait until we get a transfer done or error */
timeout = jiffies + HZ/100 + 1; /* 10ms */
while(1) {
/**
* first read usually delivers bogus results...
*/
saa7146_i2c_status(dev);
status = saa7146_i2c_status(dev);
if ((status & 0x3) != 1)
break;
if (time_after(jiffies,timeout)) {
/* this is normal when probing the bus
* (no answer from nonexisistant device...)
*/
DEB_I2C(("saa7146_i2c_writeout: timed out waiting for end of xfer\n"));
return -EIO;
}
if ((++trial < 20) && short_delay)
udelay(10);
else
msleep(1);
}
}
/* give a detailed status report */
if ( 0 != (status & SAA7146_I2C_ERR)) {
if( 0 != (status & SAA7146_I2C_SPERR) ) {
DEB_I2C(("error due to invalid start/stop condition.\n"));
}
if( 0 != (status & SAA7146_I2C_DTERR) ) {
DEB_I2C(("error in data transmission.\n"));
}
if( 0 != (status & SAA7146_I2C_DRERR) ) {
DEB_I2C(("error when receiving data.\n"));
}
if( 0 != (status & SAA7146_I2C_AL) ) {
DEB_I2C(("error because arbitration lost.\n"));
}
/* we handle address-errors here */
if( 0 != (status & SAA7146_I2C_APERR) ) {
DEB_I2C(("error in address phase.\n"));
return -EREMOTEIO;
}
return -EIO;
}
/* read back data, just in case we were reading ... */
*dword = saa7146_read(dev, I2C_TRANSFER);
DEB_I2C(("after: 0x%08x\n",*dword));
return 0;
}
int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries)
{
int i = 0, count = 0;
u32* buffer = dev->d_i2c.cpu_addr;
int err = 0;
int address_err = 0;
int short_delay = 0;
if (down_interruptible (&dev->i2c_lock))
return -ERESTARTSYS;
for(i=0;i<num;i++) {
DEB_I2C(("msg:%d/%d\n",i+1,num));
}
/* prepare the message(s), get number of u32s to transfer */
count = saa7146_i2c_msg_prepare(msgs, num, buffer);
if ( 0 > count ) {
err = -1;
goto out;
}
if ( count > 3 || 0 != (SAA7146_I2C_SHORT_DELAY & dev->ext->flags) )
short_delay = 1;
do {
/* reset the i2c-device if necessary */
err = saa7146_i2c_reset(dev);
if ( 0 > err ) {
DEB_I2C(("could not reset i2c-device.\n"));
goto out;
}
/* write out the u32s one after another */
for(i = 0; i < count; i++) {
err = saa7146_i2c_writeout(dev, &buffer[i], short_delay);
if ( 0 != err) {
/* this one is unsatisfying: some i2c slaves on some
dvb cards don't acknowledge correctly, so the saa7146
thinks that an address error occured. in that case, the
transaction should be retrying, even if an address error
occured. analog saa7146 based cards extensively rely on
i2c address probing, however, and address errors indicate that a
device is really *not* there. retrying in that case
increases the time the device needs to probe greatly, so
it should be avoided. because of the fact, that only
analog based cards use irq based i2c transactions (for dvb
cards, this screwes up other interrupt sources), we bail out
completely for analog cards after an address error and trust
the saa7146 address error detection. */
if ( -EREMOTEIO == err ) {
if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
goto out;
}
address_err++;
}
DEB_I2C(("error while sending message(s). starting again.\n"));
break;
}
}
if( 0 == err ) {
err = num;
break;
}
/* delay a bit before retrying */
msleep(10);
} while (err != num && retries--);
/* if every retry had an address error, exit right away */
if (address_err == retries) {
goto out;
}
/* if any things had to be read, get the results */
if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
DEB_I2C(("could not cleanup i2c-message.\n"));
err = -1;
goto out;
}
/* return the number of delivered messages */
DEB_I2C(("transmission successful. (msg:%d).\n",err));
out:
/* another bug in revision 0: the i2c-registers get uploaded randomly by other
uploads, so we better clear them out before continueing */
if( 0 == dev->revision ) {
u32 zero = 0;
saa7146_i2c_reset(dev);
if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
INFO(("revision 0 error. this should never happen.\n"));
}
}
up(&dev->i2c_lock);
return err;
}
/* utility functions */
static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
{
struct saa7146_dev* dev = i2c_get_adapdata(adapter);
/* use helper function to transfer data */
return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
}
/*****************************************************************************/
/* i2c-adapter helper functions */
#include <linux/i2c-id.h>
/* exported algorithm data */
static struct i2c_algorithm saa7146_algo = {
.name = "saa7146 i2c algorithm",
.id = I2C_ALGO_SAA7146,
.master_xfer = saa7146_i2c_xfer,
.functionality = saa7146_i2c_func,
};
int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate)
{
DEB_EE(("bitrate: 0x%08x\n",bitrate));
/* enable i2c-port pins */
saa7146_write(dev, MC1, (MASK_08 | MASK_24));
dev->i2c_bitrate = bitrate;
saa7146_i2c_reset(dev);
if( NULL != i2c_adapter ) {
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
i2c_adapter->data = dev;
#else
BUG_ON(!i2c_adapter->class);
i2c_set_adapdata(i2c_adapter,dev);
#endif
i2c_adapter->algo = &saa7146_algo;
i2c_adapter->algo_data = NULL;
i2c_adapter->id = I2C_ALGO_SAA7146;
i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
i2c_adapter->retries = SAA7146_I2C_RETRIES;
}
return 0;
}

View File

@@ -0,0 +1,508 @@
#include <media/saa7146_vv.h>
static int vbi_pixel_to_capture = 720 * 2;
static int vbi_workaround(struct saa7146_dev *dev)
{
struct saa7146_vv *vv = dev->vv_data;
u32 *cpu;
dma_addr_t dma_addr;
int count = 0;
int i;
DECLARE_WAITQUEUE(wait, current);
DEB_VBI(("dev:%p\n",dev));
/* once again, a bug in the saa7146: the brs acquisition
is buggy and especially the BXO-counter does not work
as specified. there is this workaround, but please
don't let me explain it. ;-) */
cpu = pci_alloc_consistent(dev->pci, 4096, &dma_addr);
if (NULL == cpu)
return -ENOMEM;
/* setup some basic programming, just for the workaround */
saa7146_write(dev, BASE_EVEN3, dma_addr);
saa7146_write(dev, BASE_ODD3, dma_addr+vbi_pixel_to_capture);
saa7146_write(dev, PROT_ADDR3, dma_addr+4096);
saa7146_write(dev, PITCH3, vbi_pixel_to_capture);
saa7146_write(dev, BASE_PAGE3, 0x0);
saa7146_write(dev, NUM_LINE_BYTE3, (2<<16)|((vbi_pixel_to_capture)<<0));
saa7146_write(dev, MC2, MASK_04|MASK_20);
/* load brs-control register */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
/* BXO = 1h, BRS to outbound */
WRITE_RPS1(0xc000008c);
/* wait for vbi_a or vbi_b*/
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
DEB_D(("...using port b\n"));
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_E_FID_B);
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | CMD_O_FID_B);
/*
WRITE_RPS1(CMD_PAUSE | MASK_09);
*/
} else {
DEB_D(("...using port a\n"));
WRITE_RPS1(CMD_PAUSE | MASK_10);
}
/* upload brs */
WRITE_RPS1(CMD_UPLOAD | MASK_08);
/* load brs-control register */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
/* BYO = 1, BXO = NQBIL (=1728 for PAL, for NTSC this is 858*2) - NumByte3 (=1440) = 288 */
WRITE_RPS1(((1728-(vbi_pixel_to_capture)) << 7) | MASK_19);
/* wait for brs_done */
WRITE_RPS1(CMD_PAUSE | MASK_08);
/* upload brs */
WRITE_RPS1(CMD_UPLOAD | MASK_08);
/* load video-dma3 NumLines3 and NumBytes3 */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (NUM_LINE_BYTE3/4));
/* dev->vbi_count*2 lines, 720 pixel (= 1440 Bytes) */
WRITE_RPS1((2 << 16) | (vbi_pixel_to_capture));
/* load brs-control register */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (BRS_CTRL/4));
/* Set BRS right: note: this is an experimental value for BXO (=> PAL!) */
WRITE_RPS1((540 << 7) | (5 << 19)); // 5 == vbi_start
/* wait for brs_done */
WRITE_RPS1(CMD_PAUSE | MASK_08);
/* upload brs and video-dma3*/
WRITE_RPS1(CMD_UPLOAD | MASK_08 | MASK_04);
/* load mc2 register: enable dma3 */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC1/4));
WRITE_RPS1(MASK_20 | MASK_04);
/* generate interrupt */
WRITE_RPS1(CMD_INTERRUPT);
/* stop rps1 */
WRITE_RPS1(CMD_STOP);
/* we have to do the workaround twice to be sure that
everything is ok */
for(i = 0; i < 2; i++) {
/* indicate to the irq handler that we do the workaround */
saa7146_write(dev, MC2, MASK_31|MASK_15);
saa7146_write(dev, NUM_LINE_BYTE3, (1<<16)|(2<<0));
saa7146_write(dev, MC2, MASK_04|MASK_20);
/* enable rps1 irqs */
SAA7146_IER_ENABLE(dev,MASK_28);
/* prepare to wait to be woken up by the irq-handler */
add_wait_queue(&vv->vbi_wq, &wait);
current->state = TASK_INTERRUPTIBLE;
/* start rps1 to enable workaround */
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
saa7146_write(dev, MC1, (MASK_13 | MASK_29));
schedule();
DEB_VBI(("brs bug workaround %d/1.\n",i));
remove_wait_queue(&vv->vbi_wq, &wait);
current->state = TASK_RUNNING;
/* disable rps1 irqs */
SAA7146_IER_DISABLE(dev,MASK_28);
/* stop video-dma3 */
saa7146_write(dev, MC1, MASK_20);
if(signal_pending(current)) {
DEB_VBI(("aborted (rps:0x%08x).\n",saa7146_read(dev,RPS_ADDR1)));
/* stop rps1 for sure */
saa7146_write(dev, MC1, MASK_29);
pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
return -EINTR;
}
}
pci_free_consistent(dev->pci, 4096, cpu, dma_addr);
return 0;
}
static void saa7146_set_vbi_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_video_dma vdma3;
int count = 0;
unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
/*
vdma3.base_even = 0xc8000000+2560*70;
vdma3.base_odd = 0xc8000000;
vdma3.prot_addr = 0xc8000000+2560*164;
vdma3.pitch = 2560;
vdma3.base_page = 0;
vdma3.num_line_byte = (64<<16)|((vbi_pixel_to_capture)<<0); // set above!
*/
vdma3.base_even = buf->pt[2].offset;
vdma3.base_odd = buf->pt[2].offset + 16 * vbi_pixel_to_capture;
vdma3.prot_addr = buf->pt[2].offset + 16 * 2 * vbi_pixel_to_capture;
vdma3.pitch = vbi_pixel_to_capture;
vdma3.base_page = buf->pt[2].dma | ME1;
vdma3.num_line_byte = (16 << 16) | vbi_pixel_to_capture;
saa7146_write_out_dma(dev, 3, &vdma3);
/* write beginning of rps-program */
count = 0;
/* wait for o_fid_a/b / e_fid_a/b toggle only if bit 1 is not set */
/* we don't wait here for the first field anymore. this is different from the video
capture and might cause that the first buffer is only half filled (with only
one field). but since this is some sort of streaming data, this is not that negative.
but by doing this, we can use the whole engine from video-buf.c... */
/*
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | e_wait);
WRITE_RPS1(CMD_PAUSE | CMD_OAN | CMD_SIG1 | o_wait);
*/
/* set bit 1 */
WRITE_RPS1(CMD_WR_REG | (1 << 8) | (MC2/4));
WRITE_RPS1(MASK_28 | MASK_12);
/* turn on video-dma3 */
WRITE_RPS1(CMD_WR_REG_MASK | (MC1/4));
WRITE_RPS1(MASK_04 | MASK_20); /* => mask */
WRITE_RPS1(MASK_04 | MASK_20); /* => values */
/* wait for o_fid_a/b / e_fid_a/b toggle */
WRITE_RPS1(CMD_PAUSE | o_wait);
WRITE_RPS1(CMD_PAUSE | e_wait);
/* generate interrupt */
WRITE_RPS1(CMD_INTERRUPT);
/* stop */
WRITE_RPS1(CMD_STOP);
/* enable rps1 irqs */
SAA7146_IER_ENABLE(dev, MASK_28);
/* write the address of the rps-program */
saa7146_write(dev, RPS_ADDR1, dev->d_rps1.dma_handle);
/* turn on rps */
saa7146_write(dev, MC1, (MASK_13 | MASK_29));
}
static int buffer_activate(struct saa7146_dev *dev,
struct saa7146_buf *buf,
struct saa7146_buf *next)
{
struct saa7146_vv *vv = dev->vv_data;
buf->vb.state = STATE_ACTIVE;
DEB_VBI(("dev:%p, buf:%p, next:%p\n",dev,buf,next));
saa7146_set_vbi_capture(dev,buf,next);
mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT);
return 0;
}
static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb,enum v4l2_field field)
{
struct file *file = q->priv_data;
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
int err = 0;
int lines, llength, size;
lines = 16 * 2 ; /* 2 fields */
llength = vbi_pixel_to_capture;
size = lines * llength;
DEB_VBI(("vb:%p\n",vb));
if (0 != buf->vb.baddr && buf->vb.bsize < size) {
DEB_VBI(("size mismatch.\n"));
return -EINVAL;
}
if (buf->vb.size != size)
saa7146_dma_free(dev,buf);
if (STATE_NEEDS_INIT == buf->vb.state) {
buf->vb.width = llength;
buf->vb.height = lines;
buf->vb.size = size;
buf->vb.field = field; // FIXME: check this
saa7146_pgtable_free(dev->pci, &buf->pt[2]);
saa7146_pgtable_alloc(dev->pci, &buf->pt[2]);
err = videobuf_iolock(dev->pci,&buf->vb, NULL);
if (err)
goto oops;
err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2], buf->vb.dma.sglist, buf->vb.dma.sglen);
if (0 != err)
return err;
}
buf->vb.state = STATE_PREPARED;
buf->activate = buffer_activate;
return 0;
oops:
DEB_VBI(("error out.\n"));
saa7146_dma_free(dev,buf);
return err;
}
static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size)
{
int llength,lines;
lines = 16 * 2 ; /* 2 fields */
llength = vbi_pixel_to_capture;
*size = lines * llength;
*count = 2;
DEB_VBI(("count:%d, size:%d\n",*count,*size));
return 0;
}
static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct file *file = q->priv_data;
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_VBI(("vb:%p\n",vb));
saa7146_buffer_queue(dev,&vv->vbi_q,buf);
}
static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb)
{
struct file *file = q->priv_data;
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_buf *buf = (struct saa7146_buf *)vb;
DEB_VBI(("vb:%p\n",vb));
saa7146_dma_free(dev,buf);
}
static struct videobuf_queue_ops vbi_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
};
/* ------------------------------------------------------------------ */
static void vbi_stop(struct saa7146_fh *fh, struct file *file)
{
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
unsigned long flags;
DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
spin_lock_irqsave(&dev->slock,flags);
/* disable rps1 */
saa7146_write(dev, MC1, MASK_29);
/* disable rps1 irqs */
SAA7146_IER_DISABLE(dev, MASK_28);
/* shut down dma 3 transfers */
saa7146_write(dev, MC1, MASK_20);
if (vv->vbi_q.curr) {
saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
}
videobuf_queue_cancel(&fh->vbi_q);
vv->vbi_streaming = NULL;
del_timer(&vv->vbi_q.timeout);
del_timer(&fh->vbi_read_timeout);
spin_unlock_irqrestore(&dev->slock, flags);
}
static void vbi_read_timeout(unsigned long data)
{
struct file *file = (struct file*)data;
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
DEB_VBI(("dev:%p, fh:%p\n",dev, fh));
vbi_stop(fh, file);
}
static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv)
{
DEB_VBI(("dev:%p\n",dev));
INIT_LIST_HEAD(&vv->vbi_q.queue);
init_timer(&vv->vbi_q.timeout);
vv->vbi_q.timeout.function = saa7146_buffer_timeout;
vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q);
vv->vbi_q.dev = dev;
init_waitqueue_head(&vv->vbi_wq);
}
static int vbi_open(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
int ret = 0;
DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
if (0 == ret) {
DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n"));
return -EBUSY;
}
/* adjust arbitrition control for video dma 3 */
arbtr_ctrl &= ~0x1f0000;
arbtr_ctrl |= 0x1d0000;
saa7146_write(dev, PCI_BT_V1, arbtr_ctrl);
saa7146_write(dev, MC2, (MASK_04|MASK_20));
memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt));
fh->vbi_fmt.sampling_rate = 27000000;
fh->vbi_fmt.offset = 248; /* todo */
fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture;
fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY;
/* fixme: this only works for PAL */
fh->vbi_fmt.start[0] = 5;
fh->vbi_fmt.count[0] = 16;
fh->vbi_fmt.start[1] = 312;
fh->vbi_fmt.count[1] = 16;
videobuf_queue_init(&fh->vbi_q, &vbi_qops,
dev->pci, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
sizeof(struct saa7146_buf),
file);
init_MUTEX(&fh->vbi_q.lock);
init_timer(&fh->vbi_read_timeout);
fh->vbi_read_timeout.function = vbi_read_timeout;
fh->vbi_read_timeout.data = (unsigned long)file;
/* initialize the brs */
if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) {
saa7146_write(dev, BRS_CTRL, MASK_30|MASK_29 | (7 << 19));
} else {
saa7146_write(dev, BRS_CTRL, 0x00000001);
if (0 != (ret = vbi_workaround(dev))) {
DEB_VBI(("vbi workaround failed!\n"));
/* return ret;*/
}
}
/* upload brs register */
saa7146_write(dev, MC2, (MASK_08|MASK_24));
return 0;
}
static void vbi_close(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
struct saa7146_vv *vv = dev->vv_data;
DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
if( fh == vv->vbi_streaming ) {
vbi_stop(fh, file);
}
saa7146_res_free(fh, RESOURCE_DMA3_BRS);
}
static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
{
struct saa7146_vv *vv = dev->vv_data;
spin_lock(&dev->slock);
if (vv->vbi_q.curr) {
DEB_VBI(("dev:%p, curr:%p\n",dev,vv->vbi_q.curr));
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount;
saa7146_buffer_finish(dev,&vv->vbi_q,STATE_DONE);
} else {
DEB_VBI(("dev:%p\n",dev));
}
saa7146_buffer_next(dev,&vv->vbi_q,1);
spin_unlock(&dev->slock);
}
static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
struct saa7146_vv *vv = dev->vv_data;
ssize_t ret = 0;
DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
if( NULL == vv->vbi_streaming ) {
// fixme: check if dma3 is available
// fixme: activate vbi engine here if necessary. (really?)
vv->vbi_streaming = fh;
}
if( fh != vv->vbi_streaming ) {
DEB_VBI(("open %p is already using vbi capture.",vv->vbi_streaming));
return -EBUSY;
}
mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
file->f_flags & O_NONBLOCK);
/*
printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
*/
return ret;
}
struct saa7146_use_ops saa7146_vbi_uops = {
.init = vbi_init,
.open = vbi_open,
.release = vbi_close,
.irq_done = vbi_irq_done,
.read = vbi_read,
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
#include <linux/module.h>
#include <media/saa7146_vv.h>
EXPORT_SYMBOL_GPL(saa7146_start_preview);
EXPORT_SYMBOL_GPL(saa7146_stop_preview);
EXPORT_SYMBOL_GPL(saa7146_set_hps_source_and_sync);
EXPORT_SYMBOL_GPL(saa7146_register_device);
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
EXPORT_SYMBOL_GPL(saa7146_vv_init);
EXPORT_SYMBOL_GPL(saa7146_vv_release);