Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

View file

@ -0,0 +1,9 @@
config VIDEO_SAA7146
tristate
depends on I2C && PCI
config VIDEO_SAA7146_VV
tristate
depends on VIDEO_V4L2
select VIDEOBUF_DMA_SG
select VIDEO_SAA7146

View file

@ -0,0 +1,5 @@
saa7146-objs := saa7146_i2c.o saa7146_core.o
saa7146_vv-objs := 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

View file

@ -0,0 +1,587 @@
/*
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.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <media/saa7146.h>
#include <linux/module.h>
static int saa7146_num;
unsigned int saa7146_debug;
module_param(saa7146_debug, uint, 0644);
MODULE_PARM_DESC(saa7146_debug, "debug level (default: 0)");
#if 0
static void dump_registers(struct saa7146_dev* dev)
{
int i = 0;
pr_info(" @ %li jiffies:\n", jiffies);
for (i = 0; i <= 0x148; i += 4)
pr_info("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 */
static inline int saa7146_wait_for_debi_done_sleep(struct saa7146_dev *dev,
unsigned long us1, unsigned long us2)
{
unsigned long timeout;
int err;
/* wait for registers to be programmed */
timeout = jiffies + usecs_to_jiffies(us1);
while (1) {
err = time_after(jiffies, timeout);
if (saa7146_read(dev, MC2) & 2)
break;
if (err) {
pr_err("%s: %s timed out while waiting for registers getting programmed\n",
dev->name, __func__);
return -ETIMEDOUT;
}
msleep(1);
}
/* wait for transfer to complete */
timeout = jiffies + usecs_to_jiffies(us2);
while (1) {
err = time_after(jiffies, timeout);
if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
break;
saa7146_read(dev, MC2);
if (err) {
DEB_S("%s: %s timed out while waiting for transfer completion\n",
dev->name, __func__);
return -ETIMEDOUT;
}
msleep(1);
}
return 0;
}
static inline int saa7146_wait_for_debi_done_busyloop(struct saa7146_dev *dev,
unsigned long us1, unsigned long us2)
{
unsigned long loops;
/* wait for registers to be programmed */
loops = us1;
while (1) {
if (saa7146_read(dev, MC2) & 2)
break;
if (!loops--) {
pr_err("%s: %s timed out while waiting for registers getting programmed\n",
dev->name, __func__);
return -ETIMEDOUT;
}
udelay(1);
}
/* wait for transfer to complete */
loops = us2 / 5;
while (1) {
if (!(saa7146_read(dev, PSR) & SPCI_DEBI_S))
break;
saa7146_read(dev, MC2);
if (!loops--) {
DEB_S("%s: %s timed out while waiting for transfer completion\n",
dev->name, __func__);
return -ETIMEDOUT;
}
udelay(5);
}
return 0;
}
int saa7146_wait_for_debi_done(struct saa7146_dev *dev, int nobusyloop)
{
if (nobusyloop)
return saa7146_wait_for_debi_done_sleep(dev, 50000, 250000);
else
return saa7146_wait_for_debi_done_busyloop(dev, 50000, 250000);
}
/****************************************************************************
* general helper functions
****************************************************************************/
/* this is videobuf_vmalloc_to_sg() from videobuf-dma-sg.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 = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
if (NULL == sglist)
return NULL;
sg_init_table(sglist, nr_pages);
for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
pg = vmalloc_to_page(virt);
if (NULL == pg)
goto err;
BUG_ON(PageHighMem(pg));
sg_set_page(&sglist[i], pg, PAGE_SIZE, 0);
}
return sglist;
err:
kfree(sglist);
return NULL;
}
/********************************************************************************/
/* common page table functions */
void *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
{
int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
void *mem = vmalloc_32(length);
int slen = 0;
if (NULL == mem)
goto err_null;
if (!(pt->slist = vmalloc_to_sg(mem, pages)))
goto err_free_mem;
if (saa7146_pgtable_alloc(pci, pt))
goto err_free_slist;
pt->nents = pages;
slen = pci_map_sg(pci,pt->slist,pt->nents,PCI_DMA_FROMDEVICE);
if (0 == slen)
goto err_free_pgtable;
if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen))
goto err_unmap_sg;
return mem;
err_unmap_sg:
pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
err_free_pgtable:
saa7146_pgtable_free(pci, pt);
err_free_slist:
kfree(pt->slist);
pt->slist = NULL;
err_free_mem:
vfree(mem);
err_null:
return NULL;
}
void saa7146_vfree_destroy_pgtable(struct pci_dev *pci, void *mem, struct saa7146_pgtable *pt)
{
pci_unmap_sg(pci, pt->slist, pt->nents, PCI_DMA_FROMDEVICE);
saa7146_pgtable_free(pci, pt);
kfree(pt->slist);
pt->slist = NULL;
vfree(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;
}
int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
{
__le32 *cpu;
dma_addr_t dma_addr = 0;
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 )
{
__le32 *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++) {
/*
pr_debug("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;
pr_debug("offset: %d\n", pt->offset);
for(i=0;i<5;i++) {
pr_debug("ptr1 %d: 0x%08x\n", i, ptr[i]);
}
*/
return 0;
}
/********************************************************************************/
/* interrupt handler */
static irqreturn_t interrupt_hw(int irq, void *dev_id)
{
struct saa7146_dev *dev = dev_id;
u32 isr;
u32 ack_isr;
/* read out the interrupt status register */
ack_isr = isr = saa7146_read(dev, ISR);
/* is this our interrupt? */
if ( 0 == isr ) {
/* nope, some other device */
return IRQ_NONE;
}
if (dev->ext) {
if (dev->ext->irq_mask & isr) {
if (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 (dev->vv_data && dev->vv_callback)
dev->vv_callback(dev,isr);
isr &= ~MASK_27;
}
if (0 != (isr & (MASK_28))) {
if (dev->vv_data && dev->vv_callback)
dev->vv_callback(dev,isr);
isr &= ~MASK_28;
}
if (0 != (isr & (MASK_16|MASK_17))) {
SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
/* only wake up if we expect something */
if (0 != dev->i2c_op) {
dev->i2c_op = 0;
wake_up(&dev->i2c_wq);
} else {
u32 psr = saa7146_read(dev, PSR);
u32 ssr = saa7146_read(dev, SSR);
pr_warn("%s: unexpected i2c irq: isr %08x psr %08x ssr %08x\n",
dev->name, isr, psr, ssr);
}
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);
}
saa7146_write(dev, ISR, ack_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;
/* clear out mem for sure */
dev = kzalloc(sizeof(struct saa7146_dev), GFP_KERNEL);
if (!dev) {
ERR("out of memory\n");
goto out;
}
/* create a nice device name */
sprintf(dev->name, "saa7146 (%d)", saa7146_num);
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 */
dev->revision = pci->revision;
/* remap the memory from virtual to physical address */
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, IRQF_SHARED,
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_zalloc_consistent(pci, SAA7146_RPS_MEM,
&dev->d_rps0.dma_handle);
if (!dev->d_rps0.cpu_addr)
goto err_free_irq;
dev->d_rps1.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM,
&dev->d_rps1.dma_handle);
if (!dev->d_rps1.cpu_addr)
goto err_free_rps0;
dev->d_i2c.cpu_addr = pci_zalloc_consistent(pci, SAA7146_RPS_MEM,
&dev->d_i2c.dma_handle);
if (!dev->d_i2c.cpu_addr)
goto err_free_rps1;
/* the rest + print status message */
pr_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;
mutex_init(&dev->v4l2_lock);
spin_lock_init(&dev->int_slock);
spin_lock_init(&dev->slock);
mutex_init(&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_free_i2c;
}
/* V4L extensions will set the pci drvdata to the v4l2_device in the
attach() above. So for those cards that do not use V4L we have to
set it explicitly. */
pci_set_drvdata(pci, &dev->v4l2_dev);
saa7146_num++;
err = 0;
out:
return err;
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 v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
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);
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;
pr_info("register extension '%s'\n", ext->name);
return pci_register_driver(&ext->driver);
}
int saa7146_unregister_extension(struct saa7146_extension* ext)
{
DEB_EE("ext:%p\n", ext);
pr_info("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_vfree_destroy_pgtable);
EXPORT_SYMBOL_GPL(saa7146_wait_for_debi_done);
EXPORT_SYMBOL_GPL(saa7146_setgpio);
EXPORT_SYMBOL_GPL(saa7146_i2c_adapter_prepare);
EXPORT_SYMBOL_GPL(saa7146_debug);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("driver for generic saa7146-based hardware");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,659 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <media/saa7146_vv.h>
#include <linux/module.h>
/****************************************************************************/
/* 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? */
if (vv->resources & bit) {
DEB_D("locked! vv->resources:0x%02x, we want:0x%02x\n",
vv->resources, bit);
/* no, someone else uses it */
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);
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;
BUG_ON((fh->resources & bits) != bits);
fh->resources &= ~bits;
vv->resources &= ~bits;
DEB_D("res: put 0x%02x, cur:0x%02x\n", bits, vv->resources);
}
/********************************************************************************/
/* common dma functions */
void saa7146_dma_free(struct saa7146_dev *dev,struct videobuf_queue *q,
struct saa7146_buf *buf)
{
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
DEB_EE("dev:%p, buf:%p\n", dev, buf);
BUG_ON(in_interrupt());
videobuf_waiton(q, &buf->vb, 0, 0);
videobuf_dma_unmap(q->dev, dma);
videobuf_dma_free(dma);
buf->vb.state = VIDEOBUF_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 = VIDEOBUF_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;
v4l2_get_timestamp(&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,VIDEOBUF_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 file *file)
{
struct video_device *vdev = video_devdata(file);
struct saa7146_dev *dev = video_drvdata(file);
struct saa7146_fh *fh = NULL;
int result = 0;
DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev));
if (mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
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 = kzalloc(sizeof(*fh),GFP_KERNEL);
if (NULL == fh) {
DEB_S("cannot allocate memory for per open data\n");
result = -ENOMEM;
goto out;
}
v4l2_fh_init(&fh->fh, vdev);
file->private_data = &fh->fh;
fh->dev = dev;
if (vdev->vfl_type == VFL_TYPE_VBI) {
DEB_S("initializing vbi...\n");
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
result = saa7146_vbi_uops.open(dev,file);
if (dev->ext_vv_data->vbi_fops.open)
dev->ext_vv_data->vbi_fops.open(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;
v4l2_fh_add(&fh->fh);
out:
if (fh && result != 0) {
kfree(fh);
file->private_data = NULL;
}
mutex_unlock(vdev->lock);
return result;
}
static int fops_release(struct file *file)
{
struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
struct saa7146_dev *dev = fh->dev;
DEB_EE("file:%p\n", file);
mutex_lock(vdev->lock);
if (vdev->vfl_type == VFL_TYPE_VBI) {
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
saa7146_vbi_uops.release(dev,file);
if (dev->ext_vv_data->vbi_fops.release)
dev->ext_vv_data->vbi_fops.release(file);
} else {
saa7146_video_uops.release(dev,file);
}
v4l2_fh_del(&fh->fh);
v4l2_fh_exit(&fh->fh);
module_put(dev->ext->module);
file->private_data = NULL;
kfree(fh);
mutex_unlock(vdev->lock);
return 0;
}
static int fops_mmap(struct file *file, struct vm_area_struct * vma)
{
struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
struct videobuf_queue *q;
int res;
switch (vdev->vfl_type) {
case VFL_TYPE_GRABBER: {
DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n",
file, vma);
q = &fh->video_q;
break;
}
case VFL_TYPE_VBI: {
DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n",
file, vma);
if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
return -ENODEV;
q = &fh->vbi_q;
break;
}
default:
BUG();
}
if (mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
res = videobuf_mmap_mapper(q, vma);
mutex_unlock(vdev->lock);
return res;
}
static unsigned int __fops_poll(struct file *file, struct poll_table_struct *wait)
{
struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
struct videobuf_buffer *buf = NULL;
struct videobuf_queue *q;
unsigned int res = v4l2_ctrl_poll(file, wait);
DEB_EE("file:%p, poll:%p\n", file, wait);
if (vdev->vfl_type == VFL_TYPE_VBI) {
if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT)
return res | POLLOUT | POLLWRNORM;
if( 0 == fh->vbi_q.streaming )
return res | 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 res | POLLERR;
}
poll_wait(file, &buf->done, wait);
if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) {
DEB_D("poll succeeded!\n");
return res | POLLIN | POLLRDNORM;
}
DEB_D("nothing to poll for, buf->state:%d\n", buf->state);
return res;
}
static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait)
{
struct video_device *vdev = video_devdata(file);
unsigned int res;
mutex_lock(vdev->lock);
res = __fops_poll(file, wait);
mutex_unlock(vdev->lock);
return res;
}
static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
int ret;
switch (vdev->vfl_type) {
case VFL_TYPE_GRABBER:
/*
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 VFL_TYPE_VBI:
/*
DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n",
file, data, (unsigned long)count);
*/
if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) {
if (mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
ret = saa7146_vbi_uops.read(file, data, count, ppos);
mutex_unlock(vdev->lock);
return ret;
}
return -EINVAL;
default:
BUG();
}
}
static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos)
{
struct video_device *vdev = video_devdata(file);
struct saa7146_fh *fh = file->private_data;
int ret;
switch (vdev->vfl_type) {
case VFL_TYPE_GRABBER:
return -EINVAL;
case VFL_TYPE_VBI:
if (fh->dev->ext_vv_data->vbi_fops.write) {
if (mutex_lock_interruptible(vdev->lock))
return -ERESTARTSYS;
ret = fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos);
mutex_unlock(vdev->lock);
return ret;
}
return -EINVAL;
default:
BUG();
}
}
static const struct v4l2_file_operations video_fops =
{
.owner = THIS_MODULE,
.open = fops_open,
.release = fops_release,
.read = fops_read,
.write = fops_write,
.poll = fops_poll,
.mmap = fops_mmap,
.unlocked_ioctl = video_ioctl2,
};
static 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 const struct v4l2_ctrl_ops saa7146_ctrl_ops = {
.s_ctrl = saa7146_s_ctrl,
};
int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
{
struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler;
struct v4l2_pix_format *fmt;
struct v4l2_vbi_format *vbi;
struct saa7146_vv *vv;
int err;
err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
if (err)
return err;
v4l2_ctrl_handler_init(hdl, 6);
v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
V4L2_CID_CONTRAST, 0, 127, 1, 64);
v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
V4L2_CID_SATURATION, 0, 127, 1, 64);
v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
V4L2_CID_VFLIP, 0, 1, 1, 0);
v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops,
V4L2_CID_HFLIP, 0, 1, 1, 0);
if (hdl->error) {
err = hdl->error;
v4l2_ctrl_handler_free(hdl);
return err;
}
dev->v4l2_dev.ctrl_handler = hdl;
vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
if (vv == NULL) {
ERR("out of memory. aborting.\n");
v4l2_ctrl_handler_free(hdl);
return -ENOMEM;
}
ext_vv->vid_ops = saa7146_video_ioctl_ops;
ext_vv->vbi_ops = saa7146_vbi_ioctl_ops;
ext_vv->core_ops = &saa7146_video_ioctl_ops;
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->d_clipping.cpu_addr =
pci_zalloc_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);
v4l2_ctrl_handler_free(hdl);
return -1;
}
saa7146_video_uops.init(dev,vv);
if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE)
saa7146_vbi_uops.init(dev,vv);
vv->ov_fb.fmt.width = vv->standard->h_max_out;
vv->ov_fb.fmt.height = vv->standard->v_max_out;
vv->ov_fb.fmt.pixelformat = V4L2_PIX_FMT_RGB565;
vv->ov_fb.fmt.bytesperline = 2 * vv->ov_fb.fmt.width;
vv->ov_fb.fmt.sizeimage = vv->ov_fb.fmt.bytesperline * vv->ov_fb.fmt.height;
vv->ov_fb.fmt.colorspace = V4L2_COLORSPACE_SRGB;
fmt = &vv->video_fmt;
fmt->width = 384;
fmt->height = 288;
fmt->pixelformat = V4L2_PIX_FMT_BGR24;
fmt->field = V4L2_FIELD_ANY;
fmt->colorspace = V4L2_COLORSPACE_SMPTE170M;
fmt->bytesperline = 3 * fmt->width;
fmt->sizeimage = fmt->bytesperline * fmt->height;
vbi = &vv->vbi_fmt;
vbi->sampling_rate = 27000000;
vbi->offset = 248; /* todo */
vbi->samples_per_line = 720 * 2;
vbi->sample_format = V4L2_PIX_FMT_GREY;
/* fixme: this only works for PAL */
vbi->start[0] = 5;
vbi->count[0] = 16;
vbi->start[1] = 312;
vbi->count[1] = 16;
init_timer(&vv->vbi_read_timeout);
vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING;
vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY;
dev->vv_data = vv;
dev->vv_callback = &vv_callback;
return 0;
}
EXPORT_SYMBOL_GPL(saa7146_vv_init);
int saa7146_vv_release(struct saa7146_dev* dev)
{
struct saa7146_vv *vv = dev->vv_data;
DEB_EE("dev:%p\n", dev);
v4l2_device_unregister(&dev->v4l2_dev);
pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
v4l2_ctrl_handler_free(&dev->ctrl_handler);
kfree(vv);
dev->vv_data = NULL;
dev->vv_callback = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(saa7146_vv_release);
int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
char *name, int type)
{
struct video_device *vfd;
int err;
int i;
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;
vfd->fops = &video_fops;
if (type == VFL_TYPE_GRABBER)
vfd->ioctl_ops = &dev->ext_vv_data->vid_ops;
else
vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops;
vfd->release = video_device_release;
vfd->lock = &dev->v4l2_lock;
vfd->v4l2_dev = &dev->v4l2_dev;
vfd->tvnorms = 0;
for (i = 0; i < dev->ext_vv_data->num_stds; i++)
vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
strlcpy(vfd->name, name, sizeof(vfd->name));
video_set_drvdata(vfd, dev);
err = video_register_device(vfd, type, -1);
if (err < 0) {
ERR("cannot register v4l2 device. skipping.\n");
video_device_release(vfd);
return err;
}
pr_info("%s: registered device %s [v4l2]\n",
dev->name, video_device_node_name(vfd));
*vid = vfd;
return 0;
}
EXPORT_SYMBOL_GPL(saa7146_register_device);
int saa7146_unregister_device(struct video_device **vid, struct saa7146_dev* dev)
{
DEB_EE("dev:%p\n", dev);
video_unregister_device(*vid);
*vid = NULL;
return 0;
}
EXPORT_SYMBOL_GPL(saa7146_unregister_device);
static int __init saa7146_vv_init_module(void)
{
return 0;
}
static void __exit saa7146_vv_cleanup_module(void)
{
}
module_init(saa7146_vv_init_module);
module_exit(saa7146_vv_cleanup_module);
MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
MODULE_DESCRIPTION("video4linux driver for saa7146-based hardware");
MODULE_LICENSE("GPL");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,423 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <media/saa7146_vv.h>
static u32 saa7146_i2c_func(struct i2c_adapter *adapter)
{
/* 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, __le32 *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) {
/* DEB_I2C("cannot prepare i2c-message\n"); */
return -ENOMEM;
}
/* be careful: clear out the i2c-mem first */
memset(op,0,sizeof(__le32)*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] |= cpu_to_le32( (u8)addr << ((3-h2)*8));
op[h1] |= cpu_to_le32(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] |= cpu_to_le32( (u32)((u8)m[i].buf[j]) << ((3-h2)*8));
op[h1] |= cpu_to_le32( 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 & (le32_to_cpu(op[h1]) >> ((3-h2)*2))) ) {
op[h1] &= ~cpu_to_le32(0x2 << ((3-h2)*2));
op[h1] |= cpu_to_le32(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, __le32 *op)
{
int i, j;
int op_count = 0;
/* loop through all messages */
for(i = 0; i < num; i++) {
op_count++;
/* loop through 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] = (le32_to_cpu(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 occurred ... */
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, __le32 *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, le32_to_cpu(*dword));
dev->i2c_op = 1;
SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
SAA7146_IER_ENABLE(dev, MASK_16|MASK_17);
saa7146_write(dev, MC2, (MASK_00 | MASK_16));
timeout = HZ/100 + 1; /* 10ms */
timeout = wait_event_interruptible_timeout(dev->i2c_wq, dev->i2c_op == 0, timeout);
if (timeout == -ERESTARTSYS || dev->i2c_op) {
SAA7146_IER_DISABLE(dev, MASK_16|MASK_17);
SAA7146_ISR_CLEAR(dev, MASK_16|MASK_17);
if (timeout == -ERESTARTSYS)
/* a signal arrived */
return -ERESTARTSYS;
pr_warn("%s %s [irq]: timed out waiting for end of xfer\n",
dev->name, __func__);
return -EIO;
}
status = saa7146_read(dev, I2C_STATUS);
} else {
saa7146_write(dev, I2C_STATUS, dev->i2c_bitrate);
saa7146_write(dev, I2C_TRANSFER, le32_to_cpu(*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)) {
pr_warn("%s %s: timed out waiting for MC2\n",
dev->name, __func__);
return -EIO;
}
}
/* wait until we get a transfer done or error */
timeout = jiffies + HZ/100 + 1; /* 10ms */
/* first read usually delivers bogus results... */
saa7146_i2c_status(dev);
while(1) {
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...)
*/
pr_warn("%s %s [poll]: timed out waiting for end of xfer\n",
dev->name, __func__);
return -EIO;
}
if (++trial < 50 && short_delay)
udelay(10);
else
msleep(1);
}
}
/* give a detailed status report */
if ( 0 != (status & (SAA7146_I2C_SPERR | SAA7146_I2C_APERR |
SAA7146_I2C_DTERR | SAA7146_I2C_DRERR |
SAA7146_I2C_AL | SAA7146_I2C_ERR |
SAA7146_I2C_BUSY)) ) {
if ( 0 == (status & SAA7146_I2C_ERR) ||
0 == (status & SAA7146_I2C_BUSY) ) {
/* it may take some time until ERR goes high - ignore */
DEB_I2C("unexpected i2c status %04x\n", status);
}
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 = cpu_to_le32(saa7146_read(dev, I2C_TRANSFER));
DEB_I2C("after: 0x%08x\n", *dword);
return 0;
}
static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *msgs, int num, int retries)
{
int i = 0, count = 0;
__le32 *buffer = dev->d_i2c.cpu_addr;
int err = 0;
int short_delay = 0;
if (mutex_lock_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 occurred. in that case, the
transaction should be retrying, even if an address error
occurred. 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. So we bail out in irq mode after an
address error and trust the saa7146 address error detection. */
if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags))
goto out;
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--);
/* quit if any error occurred */
if (err != num)
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 continuing */
if( 0 == dev->revision ) {
__le32 zero = 0;
saa7146_i2c_reset(dev);
if( 0 != saa7146_i2c_writeout(dev, &zero, short_delay)) {
pr_info("revision 0 error. this should never happen\n");
}
}
mutex_unlock(&dev->i2c_lock);
return err;
}
/* utility functions */
static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
{
struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
/* use helper function to transfer data */
return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
}
/*****************************************************************************/
/* i2c-adapter helper functions */
/* exported algorithm data */
static struct i2c_algorithm saa7146_algo = {
.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 (i2c_adapter) {
i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev);
i2c_adapter->dev.parent = &dev->pci->dev;
i2c_adapter->algo = &saa7146_algo;
i2c_adapter->algo_data = NULL;
i2c_adapter->timeout = SAA7146_I2C_TIMEOUT;
i2c_adapter->retries = SAA7146_I2C_RETRIES;
}
return 0;
}

View file

@ -0,0 +1,498 @@
#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 videobuf-dma-sg.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 = VIDEOBUF_ACTIVE;
DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next);
saa7146_set_vbi_capture(dev,buf,next);
mod_timer(&vv->vbi_dmaq.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,q,buf);
if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
struct videobuf_dmabuf *dma=videobuf_to_dma(&buf->vb);
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(q,&buf->vb, NULL);
if (err)
goto oops;
err = saa7146_pgtable_build_single(dev->pci, &buf->pt[2],
dma->sglist, dma->sglen);
if (0 != err)
return err;
}
buf->vb.state = VIDEOBUF_PREPARED;
buf->activate = buffer_activate;
return 0;
oops:
DEB_VBI("error out\n");
saa7146_dma_free(dev,q,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_dmaq, 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,q,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_dmaq.curr)
saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
videobuf_queue_cancel(&fh->vbi_q);
vv->vbi_streaming = NULL;
del_timer(&vv->vbi_dmaq.timeout);
del_timer(&vv->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_dmaq.queue);
init_timer(&vv->vbi_dmaq.timeout);
vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout;
vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq);
vv->vbi_dmaq.dev = dev;
init_waitqueue_head(&vv->vbi_wq);
}
static int vbi_open(struct saa7146_dev *dev, struct file *file)
{
struct saa7146_fh *fh = file->private_data;
struct saa7146_vv *vv = fh->dev->vv_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));
videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops,
&dev->pci->dev, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
sizeof(struct saa7146_buf),
file, &dev->v4l2_lock);
vv->vbi_read_timeout.function = vbi_read_timeout;
vv->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 = 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_dmaq.curr) {
DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr);
/* this must be += 2, one count for each field */
vv->vbi_fieldcount+=2;
vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount;
saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE);
} else {
DEB_VBI("dev:%p\n", dev);
}
saa7146_buffer_next(dev, &vv->vbi_dmaq, 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\n",
vv->vbi_streaming);
return -EBUSY;
}
mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT);
ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1,
file->f_flags & O_NONBLOCK);
/*
printk("BASE_ODD3: 0x%08x\n", saa7146_read(dev, BASE_ODD3));
printk("BASE_EVEN3: 0x%08x\n", saa7146_read(dev, BASE_EVEN3));
printk("PROT_ADDR3: 0x%08x\n", saa7146_read(dev, PROT_ADDR3));
printk("PITCH3: 0x%08x\n", saa7146_read(dev, PITCH3));
printk("BASE_PAGE3: 0x%08x\n", saa7146_read(dev, BASE_PAGE3));
printk("NUM_LINE_BYTE3: 0x%08x\n", saa7146_read(dev, NUM_LINE_BYTE3));
printk("BRS_CTRL: 0x%08x\n", saa7146_read(dev, BRS_CTRL));
*/
return ret;
}
struct saa7146_use_ops saa7146_vbi_uops = {
.init = vbi_init,
.open = vbi_open,
.release = vbi_close,
.irq_done = vbi_irq_done,
.read = vbi_read,
};

File diff suppressed because it is too large Load diff