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:
12
drivers/media/common/Kconfig
Normal file
12
drivers/media/common/Kconfig
Normal 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
|
6
drivers/media/common/Makefile
Normal file
6
drivers/media/common/Makefile
Normal 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
|
381
drivers/media/common/ir-common.c
Normal file
381
drivers/media/common/ir-common.c
Normal 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:
|
||||
*/
|
547
drivers/media/common/saa7146_core.c
Normal file
547
drivers/media/common/saa7146_core.c
Normal 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");
|
564
drivers/media/common/saa7146_fops.c
Normal file
564
drivers/media/common/saa7146_fops.c
Normal 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");
|
1036
drivers/media/common/saa7146_hlp.c
Normal file
1036
drivers/media/common/saa7146_hlp.c
Normal file
File diff suppressed because it is too large
Load Diff
421
drivers/media/common/saa7146_i2c.c
Normal file
421
drivers/media/common/saa7146_i2c.c
Normal 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;
|
||||
}
|
508
drivers/media/common/saa7146_vbi.c
Normal file
508
drivers/media/common/saa7146_vbi.c
Normal 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,
|
||||
};
|
1509
drivers/media/common/saa7146_video.c
Normal file
1509
drivers/media/common/saa7146_video.c
Normal file
File diff suppressed because it is too large
Load Diff
12
drivers/media/common/saa7146_vv_ksyms.c
Normal file
12
drivers/media/common/saa7146_vv_ksyms.c
Normal 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);
|
Reference in New Issue
Block a user