mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-10-30 07:38:52 +01:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
9
drivers/media/common/saa7146/Kconfig
Normal file
9
drivers/media/common/saa7146/Kconfig
Normal 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
|
||||
5
drivers/media/common/saa7146/Makefile
Normal file
5
drivers/media/common/saa7146/Makefile
Normal 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
|
||||
587
drivers/media/common/saa7146/saa7146_core.c
Normal file
587
drivers/media/common/saa7146/saa7146_core.c
Normal 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");
|
||||
659
drivers/media/common/saa7146/saa7146_fops.c
Normal file
659
drivers/media/common/saa7146/saa7146_fops.c
Normal 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");
|
||||
1048
drivers/media/common/saa7146/saa7146_hlp.c
Normal file
1048
drivers/media/common/saa7146/saa7146_hlp.c
Normal file
File diff suppressed because it is too large
Load diff
423
drivers/media/common/saa7146/saa7146_i2c.c
Normal file
423
drivers/media/common/saa7146/saa7146_i2c.c
Normal 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;
|
||||
}
|
||||
498
drivers/media/common/saa7146/saa7146_vbi.c
Normal file
498
drivers/media/common/saa7146/saa7146_vbi.c
Normal 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,
|
||||
};
|
||||
1309
drivers/media/common/saa7146/saa7146_video.c
Normal file
1309
drivers/media/common/saa7146/saa7146_video.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue