summaryrefslogtreecommitdiff
path: root/drivers/media/video/video-buf.c
diff options
context:
space:
mode:
authorMauro Carvalho Chehab <mchehab@infradead.org>2006-03-10 12:29:15 -0300
committerMauro Carvalho Chehab <mchehab@infradead.org>2006-03-24 16:26:49 -0300
commitc7b0ac0546985fc6361a8d92cf808d46da797677 (patch)
tree2147eb2f6dd7b2b82e0d7b4b9a26e12ef5193252 /drivers/media/video/video-buf.c
parentb2fd16b4ff2508ac16ae994f4bcd941f97754c00 (diff)
downloadlinux-c7b0ac0546985fc6361a8d92cf808d46da797677.tar.gz
linux-c7b0ac0546985fc6361a8d92cf808d46da797677.tar.xz
V4L/DVB (3516): Make video_buf more generic
Video_buf were concerned to allow PCI devices to be used as video capture devices. This patch extends video_buf features by virtualizing pci-dependent functions and allowing other type of devices to use it. It is still DMA centric, although it may be used also by devices that emulates scatter/gather behavior or a DMA device Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
Diffstat (limited to 'drivers/media/video/video-buf.c')
-rw-r--r--drivers/media/video/video-buf.c250
1 files changed, 187 insertions, 63 deletions
diff --git a/drivers/media/video/video-buf.c b/drivers/media/video/video-buf.c
index 87e937581d5a..d2ca0f08d0df 100644
--- a/drivers/media/video/video-buf.c
+++ b/drivers/media/video/video-buf.c
@@ -1,15 +1,20 @@
/*
*
* generic helper functions for video4linux capture buffers, to handle
- * memory management and PCI DMA. Right now bttv + saa7134 use it.
+ * memory management and PCI DMA.
+ * Right now, bttv, saa7134, saa7146 and cx88 use it.
*
* The functions expect the hardware being able to scatter gatter
* (i.e. the buffers are not linear in physical memory, but fragmented
* into PAGE_SIZE chunks). They also assume the driver does not need
- * to touch the video data (thus it is probably not useful for USB 1.1
- * as data often must be uncompressed by the drivers).
+ * to touch the video data.
+ *
+ * device specific map/unmap/sync stuff now are mapped as operations
+ * to allow its usage by USB and virtual devices.
*
* (c) 2001-2004 Gerd Knorr <kraxel@bytesex.org> [SUSE Labs]
+ * (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * (c) 2006 Ted Walther and John Sokol
*
* 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
@@ -167,6 +172,9 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction,
dprintk(1,"vmalloc_32(%d pages) failed\n",nr_pages);
return -ENOMEM;
}
+ dprintk(1,"vmalloc is at addr 0x%08lx, size=%d\n",
+ (unsigned long)dma->vmalloc,
+ nr_pages << PAGE_SHIFT);
memset(dma->vmalloc,0,nr_pages << PAGE_SHIFT);
dma->nr_pages = nr_pages;
return 0;
@@ -186,8 +194,10 @@ int videobuf_dma_init_overlay(struct videobuf_dmabuf *dma, int direction,
return 0;
}
-int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma)
+int videobuf_dma_map(struct videobuf_queue* q,struct videobuf_dmabuf *dma)
{
+ void *dev=q->dev;
+
MAGIC_CHECK(dma->magic,MAGIC_DMABUF);
BUG_ON(0 == dma->nr_pages);
@@ -197,7 +207,7 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma)
}
if (dma->vmalloc) {
dma->sglist = videobuf_vmalloc_to_sg
- (dma->vmalloc,dma->nr_pages);
+ (dma->vmalloc,dma->nr_pages);
}
if (dma->bus_addr) {
dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
@@ -212,13 +222,14 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma)
dprintk(1,"scatterlist is NULL\n");
return -ENOMEM;
}
-
if (!dma->bus_addr) {
- dma->sglen = pci_map_sg(dev,dma->sglist,dma->nr_pages,
- dma->direction);
+ if (q->ops->vb_map_sg) {
+ dma->sglen = q->ops->vb_map_sg(dev,dma->sglist,
+ dma->nr_pages, dma->direction);
+ }
if (0 == dma->sglen) {
printk(KERN_WARNING
- "%s: pci_map_sg failed\n",__FUNCTION__);
+ "%s: videobuf_map_sg failed\n",__FUNCTION__);
kfree(dma->sglist);
dma->sglist = NULL;
dma->sglen = 0;
@@ -228,24 +239,31 @@ int videobuf_dma_pci_map(struct pci_dev *dev, struct videobuf_dmabuf *dma)
return 0;
}
-int videobuf_dma_pci_sync(struct pci_dev *dev, struct videobuf_dmabuf *dma)
+int videobuf_dma_sync(struct videobuf_queue* q,struct videobuf_dmabuf *dma)
{
+ void *dev=q->dev;
+
MAGIC_CHECK(dma->magic,MAGIC_DMABUF);
BUG_ON(!dma->sglen);
- if (!dma->bus_addr)
- pci_dma_sync_sg_for_cpu(dev,dma->sglist,dma->nr_pages,dma->direction);
+ if (!dma->bus_addr && q->ops->vb_dma_sync_sg)
+ q->ops->vb_dma_sync_sg(dev,dma->sglist,dma->nr_pages,
+ dma->direction);
+
return 0;
}
-int videobuf_dma_pci_unmap(struct pci_dev *dev, struct videobuf_dmabuf *dma)
+int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma)
{
+ void *dev=q->dev;
+
MAGIC_CHECK(dma->magic,MAGIC_DMABUF);
if (!dma->sglen)
return 0;
- if (!dma->bus_addr)
- pci_unmap_sg(dev,dma->sglist,dma->nr_pages,dma->direction);
+ if (!dma->bus_addr && q->ops->vb_unmap_sg)
+ q->ops->vb_unmap_sg(dev,dma->sglist,dma->nr_pages,
+ dma->direction);
kfree(dma->sglist);
dma->sglist = NULL;
dma->sglen = 0;
@@ -318,7 +336,7 @@ int videobuf_waiton(struct videobuf_buffer *vb, int non_blocking, int intr)
}
int
-videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb,
+videobuf_iolock(struct videobuf_queue* q, struct videobuf_buffer *vb,
struct v4l2_framebuffer *fbuf)
{
int err,pages;
@@ -357,7 +375,7 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb,
default:
BUG();
}
- err = videobuf_dma_pci_map(pci,&vb->dma);
+ err = videobuf_dma_map(q,&vb->dma);
if (0 != err)
return err;
@@ -366,9 +384,41 @@ videobuf_iolock(struct pci_dev *pci, struct videobuf_buffer *vb,
/* --------------------------------------------------------------------- */
+void videobuf_queue_pci(struct videobuf_queue* q)
+{
+ /* If not specified, defaults to PCI map sg */
+ if (!q->ops->vb_map_sg)
+ q->ops->vb_map_sg=(vb_map_sg_t *)pci_map_sg;
+
+ if (!q->ops->vb_dma_sync_sg)
+ q->ops->vb_dma_sync_sg=(vb_map_sg_t *)pci_dma_sync_sg_for_cpu;
+ if (!q->ops->vb_unmap_sg)
+ q->ops->vb_unmap_sg=(vb_map_sg_t *)pci_unmap_sg;
+}
+
+int videobuf_pci_dma_map(struct pci_dev *pci,struct videobuf_dmabuf *dma)
+{
+ struct videobuf_queue q;
+
+ q.dev=pci;
+ q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg;
+
+ return (videobuf_dma_unmap(&q,dma));
+}
+
+int videobuf_pci_dma_unmap(struct pci_dev *pci,struct videobuf_dmabuf *dma)
+{
+ struct videobuf_queue q;
+
+ q.dev=pci;
+ q.ops->vb_map_sg=(vb_map_sg_t *)pci_unmap_sg;
+
+ return (videobuf_dma_unmap(&q,dma));
+}
+
void videobuf_queue_init(struct videobuf_queue* q,
struct videobuf_queue_ops *ops,
- struct pci_dev *pci,
+ void *dev,
spinlock_t *irqlock,
enum v4l2_buf_type type,
enum v4l2_field field,
@@ -377,13 +427,15 @@ void videobuf_queue_init(struct videobuf_queue* q,
{
memset(q,0,sizeof(*q));
q->irqlock = irqlock;
- q->pci = pci;
+ q->dev = dev;
q->type = type;
q->field = field;
q->msize = msize;
q->ops = ops;
q->priv_data = priv;
+ videobuf_queue_pci(q);
+
mutex_init(&q->lock);
INIT_LIST_HEAD(&q->stream);
}
@@ -431,7 +483,8 @@ videobuf_queue_cancel(struct videobuf_queue *q)
int i;
/* remove queued buffers from list */
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
if (NULL == q->bufs[i])
continue;
@@ -440,7 +493,8 @@ videobuf_queue_cancel(struct videobuf_queue *q)
q->bufs[i]->state = STATE_ERROR;
}
}
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
/* free all buffers + clear queue */
for (i = 0; i < VIDEO_MAX_FRAME; i++) {
@@ -534,19 +588,29 @@ videobuf_reqbufs(struct videobuf_queue *q,
unsigned int size,count;
int retval;
- if (req->type != q->type)
+ if (req->type != q->type) {
+ dprintk(1,"reqbufs: queue type invalid\n");
return -EINVAL;
- if (req->count < 1)
+ }
+ if (req->count < 1) {
+ dprintk(1,"reqbufs: count invalid (%d)\n",req->count);
return -EINVAL;
+ }
if (req->memory != V4L2_MEMORY_MMAP &&
req->memory != V4L2_MEMORY_USERPTR &&
- req->memory != V4L2_MEMORY_OVERLAY)
+ req->memory != V4L2_MEMORY_OVERLAY) {
+ dprintk(1,"reqbufs: memory type invalid\n");
return -EINVAL;
+ }
- if (q->streaming)
+ if (q->streaming) {
+ dprintk(1,"reqbufs: streaming already exists\n");
return -EBUSY;
- if (!list_empty(&q->stream))
+ }
+ if (!list_empty(&q->stream)) {
+ dprintk(1,"reqbufs: stream running\n");
return -EBUSY;
+ }
mutex_lock(&q->lock);
count = req->count;
@@ -559,8 +623,10 @@ videobuf_reqbufs(struct videobuf_queue *q,
count, size, (count*size)>>PAGE_SHIFT);
retval = videobuf_mmap_setup(q,count,size,req->memory);
- if (retval < 0)
+ if (retval < 0) {
+ dprintk(1,"reqbufs: mmap setup returned %d\n",retval);
goto done;
+ }
req->count = count;
@@ -572,12 +638,18 @@ videobuf_reqbufs(struct videobuf_queue *q,
int
videobuf_querybuf(struct videobuf_queue *q, struct v4l2_buffer *b)
{
- if (unlikely(b->type != q->type))
+ if (unlikely(b->type != q->type)) {
+ dprintk(1,"querybuf: Wrong type.\n");
return -EINVAL;
- if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME))
+ }
+ if (unlikely(b->index < 0 || b->index >= VIDEO_MAX_FRAME)) {
+ dprintk(1,"querybuf: index out of range.\n");
return -EINVAL;
- if (unlikely(NULL == q->bufs[b->index]))
+ }
+ if (unlikely(NULL == q->bufs[b->index])) {
+ dprintk(1,"querybuf: buffer is null.\n");
return -EINVAL;
+ }
videobuf_status(b,q->bufs[b->index],q->type);
return 0;
}
@@ -593,26 +665,40 @@ videobuf_qbuf(struct videobuf_queue *q,
mutex_lock(&q->lock);
retval = -EBUSY;
- if (q->reading)
+ if (q->reading) {
+ dprintk(1,"qbuf: Reading running...\n");
goto done;
+ }
retval = -EINVAL;
- if (b->type != q->type)
+ if (b->type != q->type) {
+ dprintk(1,"qbuf: Wrong type.\n");
goto done;
- if (b->index < 0 || b->index >= VIDEO_MAX_FRAME)
+ }
+ if (b->index < 0 || b->index >= VIDEO_MAX_FRAME) {
+ dprintk(1,"qbuf: index out of range.\n");
goto done;
+ }
buf = q->bufs[b->index];
- if (NULL == buf)
+ if (NULL == buf) {
+ dprintk(1,"qbuf: buffer is null.\n");
goto done;
+ }
MAGIC_CHECK(buf->magic,MAGIC_BUFFER);
- if (buf->memory != b->memory)
+ if (buf->memory != b->memory) {
+ dprintk(1,"qbuf: memory type is wrong.\n");
goto done;
+ }
if (buf->state == STATE_QUEUED ||
- buf->state == STATE_ACTIVE)
+ buf->state == STATE_ACTIVE) {
+ dprintk(1,"qbuf: buffer is already queued or active.\n");
goto done;
+ }
if (b->flags & V4L2_BUF_FLAG_INPUT) {
- if (b->input >= q->inputs)
+ if (b->input >= q->inputs) {
+ dprintk(1,"qbuf: wrong input.\n");
goto done;
+ }
buf->input = b->input;
} else {
buf->input = UNSET;
@@ -620,12 +706,16 @@ videobuf_qbuf(struct videobuf_queue *q,
switch (b->memory) {
case V4L2_MEMORY_MMAP:
- if (0 == buf->baddr)
+ if (0 == buf->baddr) {
+ dprintk(1,"qbuf: mmap requested but buffer addr is zero!\n");
goto done;
+ }
break;
case V4L2_MEMORY_USERPTR:
- if (b->length < buf->bsize)
+ if (b->length < buf->bsize) {
+ dprintk(1,"qbuf: buffer length is not enough\n");
goto done;
+ }
if (STATE_NEEDS_INIT != buf->state && buf->baddr != b->m.userptr)
q->ops->buf_release(q,buf);
buf->baddr = b->m.userptr;
@@ -634,20 +724,27 @@ videobuf_qbuf(struct videobuf_queue *q,
buf->boff = b->m.offset;
break;
default:
+ dprintk(1,"qbuf: wrong memory type\n");
goto done;
}
+ dprintk(1,"qbuf: requesting next field\n");
field = videobuf_next_field(q);
retval = q->ops->buf_prepare(q,buf,field);
- if (0 != retval)
+ if (0 != retval) {
+ dprintk(1,"qbuf: buffer_prepare returned %d\n",retval);
goto done;
+ }
list_add_tail(&buf->stream,&q->stream);
if (q->streaming) {
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(q,buf);
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
}
+ dprintk(1,"qbuf: succeded\n");
retval = 0;
done:
@@ -664,26 +761,39 @@ videobuf_dqbuf(struct videobuf_queue *q,
mutex_lock(&q->lock);
retval = -EBUSY;
- if (q->reading)
+ if (q->reading) {
+ dprintk(1,"dqbuf: Reading running...\n");
goto done;
+ }
retval = -EINVAL;
- if (b->type != q->type)
+ if (b->type != q->type) {
+ dprintk(1,"dqbuf: Wrong type.\n");
goto done;
- if (list_empty(&q->stream))
+ }
+ if (list_empty(&q->stream)) {
+ dprintk(1,"dqbuf: stream running\n");
goto done;
+ }
buf = list_entry(q->stream.next, struct videobuf_buffer, stream);
retval = videobuf_waiton(buf, nonblocking, 1);
- if (retval < 0)
+ if (retval < 0) {
+ dprintk(1,"dqbuf: waiton returned %d\n",retval);
goto done;
+ }
switch (buf->state) {
case STATE_ERROR:
+ dprintk(1,"dqbuf: state is error\n");
retval = -EIO;
- /* fall through */
+ videobuf_dma_sync(q,&buf->dma);
+ buf->state = STATE_IDLE;
+ break;
case STATE_DONE:
- videobuf_dma_pci_sync(q->pci,&buf->dma);
+ dprintk(1,"dqbuf: state is done\n");
+ videobuf_dma_sync(q,&buf->dma);
buf->state = STATE_IDLE;
break;
default:
+ dprintk(1,"dqbuf: state invalid\n");
retval = -EINVAL;
goto done;
}
@@ -711,13 +821,15 @@ int videobuf_streamon(struct videobuf_queue *q)
if (q->streaming)
goto done;
q->streaming = 1;
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
list_for_each(list,&q->stream) {
buf = list_entry(list, struct videobuf_buffer, stream);
if (buf->state == STATE_PREPARED)
q->ops->buf_queue(q,buf);
}
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
done:
mutex_unlock(&q->lock);
@@ -762,12 +874,14 @@ videobuf_read_zerocopy(struct videobuf_queue *q, char __user *data,
goto done;
/* start capture & wait */
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(q,q->read_buf);
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
retval = videobuf_waiton(q->read_buf,0,0);
if (0 == retval) {
- videobuf_dma_pci_sync(q->pci,&q->read_buf->dma);
+ videobuf_dma_sync(q,&q->read_buf->dma);
if (STATE_ERROR == q->read_buf->state)
retval = -EIO;
else
@@ -809,6 +923,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
/* need to capture a new frame */
retval = -ENOMEM;
q->read_buf = videobuf_alloc(q->msize);
+ dprintk(1,"video alloc=0x%08x\n",(unsigned int) q->read_buf);
if (NULL == q->read_buf)
goto done;
q->read_buf->memory = V4L2_MEMORY_USERPTR;
@@ -820,9 +935,11 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
q->read_buf = NULL;
goto done;
}
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(q,q->read_buf);
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
q->read_off = 0;
}
@@ -830,7 +947,7 @@ ssize_t videobuf_read_one(struct videobuf_queue *q,
retval = videobuf_waiton(q->read_buf, nonblocking, 1);
if (0 != retval)
goto done;
- videobuf_dma_pci_sync(q->pci,&q->read_buf->dma);
+ videobuf_dma_sync(q,&q->read_buf->dma);
if (STATE_ERROR == q->read_buf->state) {
/* catch I/O errors */
@@ -887,10 +1004,12 @@ int videobuf_read_start(struct videobuf_queue *q)
return err;
list_add_tail(&q->bufs[i]->stream, &q->stream);
}
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
for (i = 0; i < count; i++)
q->ops->buf_queue(q,q->bufs[i]);
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
q->reading = 1;
return 0;
}
@@ -985,9 +1104,11 @@ ssize_t videobuf_read_stream(struct videobuf_queue *q,
if (q->read_off == q->read_buf->size) {
list_add_tail(&q->read_buf->stream,
&q->stream);
- spin_lock_irqsave(q->irqlock,flags);
+ if (q->irqlock)
+ spin_lock_irqsave(q->irqlock,flags);
q->ops->buf_queue(q,q->read_buf);
- spin_unlock_irqrestore(q->irqlock,flags);
+ if (q->irqlock)
+ spin_unlock_irqrestore(q->irqlock,flags);
q->read_buf = NULL;
}
if (retval < 0)
@@ -1249,11 +1370,14 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init);
EXPORT_SYMBOL_GPL(videobuf_dma_init_user);
EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel);
EXPORT_SYMBOL_GPL(videobuf_dma_init_overlay);
-EXPORT_SYMBOL_GPL(videobuf_dma_pci_map);
-EXPORT_SYMBOL_GPL(videobuf_dma_pci_sync);
-EXPORT_SYMBOL_GPL(videobuf_dma_pci_unmap);
+EXPORT_SYMBOL_GPL(videobuf_dma_map);
+EXPORT_SYMBOL_GPL(videobuf_dma_sync);
+EXPORT_SYMBOL_GPL(videobuf_dma_unmap);
EXPORT_SYMBOL_GPL(videobuf_dma_free);
+EXPORT_SYMBOL_GPL(videobuf_pci_dma_map);
+EXPORT_SYMBOL_GPL(videobuf_pci_dma_unmap);
+
EXPORT_SYMBOL_GPL(videobuf_alloc);
EXPORT_SYMBOL_GPL(videobuf_waiton);
EXPORT_SYMBOL_GPL(videobuf_iolock);