diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 15:20:36 -0700 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/media/common |
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'drivers/media/common')
-rw-r--r-- | drivers/media/common/Kconfig | 12 | ||||
-rw-r--r-- | drivers/media/common/Makefile | 6 | ||||
-rw-r--r-- | drivers/media/common/ir-common.c | 381 | ||||
-rw-r--r-- | drivers/media/common/saa7146_core.c | 547 | ||||
-rw-r--r-- | drivers/media/common/saa7146_fops.c | 564 | ||||
-rw-r--r-- | drivers/media/common/saa7146_hlp.c | 1036 | ||||
-rw-r--r-- | drivers/media/common/saa7146_i2c.c | 421 | ||||
-rw-r--r-- | drivers/media/common/saa7146_vbi.c | 508 | ||||
-rw-r--r-- | drivers/media/common/saa7146_video.c | 1509 | ||||
-rw-r--r-- | drivers/media/common/saa7146_vv_ksyms.c | 12 |
10 files changed, 4996 insertions, 0 deletions
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig new file mode 100644 index 00000000000..caebd0a1c02 --- /dev/null +++ b/drivers/media/common/Kconfig @@ -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 diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile new file mode 100644 index 00000000000..97b4341255e --- /dev/null +++ b/drivers/media/common/Makefile @@ -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 diff --git a/drivers/media/common/ir-common.c b/drivers/media/common/ir-common.c new file mode 100644 index 00000000000..8c842e2f59a --- /dev/null +++ b/drivers/media/common/ir-common.c @@ -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: + */ diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c new file mode 100644 index 00000000000..9f6c19ac128 --- /dev/null +++ b/drivers/media/common/saa7146_core.c @@ -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"); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c new file mode 100644 index 00000000000..cb826c9adfe --- /dev/null +++ b/drivers/media/common/saa7146_fops.c @@ -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)); |