video-buf: physically contiguous buffers
- From: Kevin Hilman <khilman (at) mvista.com>
- Date: Thu, 04 May 2006 12:02:41 -0700
Mauro,
OK, here's a patch implementing support for physically contiguous buffers. A
second patch is attached which hacks up vivi.c to show how to use the
contiguous buffers.
So far, I've only tested this only on an embedded ARM target. I also used the
capture.c from the v4l2 site for testing, but modified it to capture at 320x240
(otherwise my ARM target runs out of memory and get_free_pages() fails.) Note
corresponding hack to vivi.c as well.
Kevin
Index: v4l-dvb/linux/drivers/media/common/saa7146_vbi.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/common/saa7146_vbi.c
+++ v4l-dvb/linux/drivers/media/common/saa7146_vbi.c
(at) (at) -410,6 +410,7 (at) (at) static int vbi_open(struct saa7146_dev *
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB, // FIXME: does this really work?
sizeof(struct saa7146_buf),
+ VIDEOBUF_BUF_FRAGMENTED,
file);
mutex_init(&fh->vbi_q.lock);
Index: v4l-dvb/linux/drivers/media/common/saa7146_video.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/common/saa7146_video.c
+++ v4l-dvb/linux/drivers/media/common/saa7146_video.c
(at) (at) -1415,6 +1415,7 (at) (at) static int video_open(struct saa7146_dev
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct saa7146_buf),
+ VIDEOBUF_BUF_FRAGMENTED,
file);
mutex_init(&fh->video_q.lock);
Index: v4l-dvb/linux/drivers/media/video/bt8xx/bttv-driver.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/bt8xx/bttv-driver.c
+++ v4l-dvb/linux/drivers/media/video/bt8xx/bttv-driver.c
(at) (at) -3124,12 +3124,14 (at) (at) static int bttv_open(struct inode *inode
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct bttv_buffer),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
videobuf_queue_init(&fh->vbi, &bttv_vbi_qops,
btv->c.pci, &btv->s_lock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB,
sizeof(struct bttv_buffer),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
i2c_vidiocschan(btv);
Index: v4l-dvb/linux/drivers/media/video/cx88/cx88-blackbird.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/cx88/cx88-blackbird.c
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-blackbird.c
(at) (at) -1557,6 +1557,7 (at) (at) static int mpeg_open(struct inode *inode
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx88_buffer),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
/* FIXME: locking against other video device */
Index: v4l-dvb/linux/drivers/media/video/cx88/cx88-dvb.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/cx88/cx88-dvb.c
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-dvb.c
(at) (at) -781,6 +781,7 (at) (at) static int __devinit dvb_probe(struct pc
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_TOP,
sizeof(struct cx88_buffer),
+ VIDEOBUF_BUF_FRAGMENTED,
dev);
err = dvb_register(dev);
if (0 != err)
Index: v4l-dvb/linux/drivers/media/video/cx88/cx88-video.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/cx88/cx88-video.c
+++ v4l-dvb/linux/drivers/media/video/cx88/cx88-video.c
(at) (at) -1014,12 +1014,14 (at) (at) static int video_open(struct inode *inod
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct cx88_buffer),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
videobuf_queue_init(&fh->vbiq, &cx8800_vbi_qops,
dev->pci, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB,
sizeof(struct cx88_buffer),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
if (fh->radio) {
Index: v4l-dvb/linux/drivers/media/video/saa7134/saa7134-dvb.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/saa7134/saa7134-dvb.c
+++ v4l-dvb/linux/drivers/media/video/saa7134/saa7134-dvb.c
(at) (at) -1017,6 +1017,7 (at) (at) static int dvb_init(struct saa7134_dev *
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_ALTERNATE,
sizeof(struct saa7134_buf),
+ VIDEOBUF_BUF_FRAGMENTED,
dev);
switch (dev->board) {
Index: v4l-dvb/linux/drivers/media/video/saa7134/saa7134-empress.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/saa7134/saa7134-empress.c
+++ v4l-dvb/linux/drivers/media/video/saa7134/saa7134-empress.c
(at) (at) -387,6 +387,7 (at) (at) static int empress_init(struct saa7134_d
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_ALTERNATE,
sizeof(struct saa7134_buf),
+ VIDEOBUF_BUF_FRAGMENTED,
dev);
empress_signal_update(dev);
Index: v4l-dvb/linux/drivers/media/video/saa7134/saa7134-video.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/saa7134/saa7134-video.c
+++ v4l-dvb/linux/drivers/media/video/saa7134/saa7134-video.c
(at) (at) -1282,12 +1282,14 (at) (at) static int video_open(struct inode *inod
V4L2_BUF_TYPE_VIDEO_CAPTURE,
V4L2_FIELD_INTERLACED,
sizeof(struct saa7134_buf),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
videobuf_queue_init(&fh->vbi, &saa7134_vbi_qops,
dev->pci, &dev->slock,
V4L2_BUF_TYPE_VBI_CAPTURE,
V4L2_FIELD_SEQ_TB,
sizeof(struct saa7134_buf),
+ VIDEOBUF_BUF_FRAGMENTED,
fh);
saa7134_pgtable_alloc(dev->pci,&fh->pt_cap);
saa7134_pgtable_alloc(dev->pci,&fh->pt_vbi);
Index: v4l-dvb/linux/drivers/media/video/video-buf.c
===================================================================
--- v4l-dvb.orig/linux/drivers/media/video/video-buf.c
+++ v4l-dvb/linux/drivers/media/video/video-buf.c
(at) (at) -4,10 +4,7 (at) (at)
* 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.
+ * The functions assume the driver does not need 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.
(at) (at) -244,6 +241,9 (at) (at) int videobuf_dma_sync(struct videobuf_qu
{
void *dev=q->dev;
+ if (q->buf_type == VIDEOBUF_BUF_LINEAR)
+ return;
+
MAGIC_CHECK(dma->magic,MAGIC_DMABUF);
BUG_ON(!dma->sglen);
(at) (at) -435,6 +435,7 (at) (at) void videobuf_queue_init(struct videobuf
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
+ enum videobuf_buf_type buf_type,
void *priv)
{
memset(q,0,sizeof(*q));
(at) (at) -445,6 +446,7 (at) (at) void videobuf_queue_init(struct videobuf
q->msize = msize;
q->ops = ops;
q->priv_data = priv;
+ q->buf_type = buf_type;
videobuf_queue_pci(q);
(at) (at) -640,6 +642,16 (at) (at) videobuf_reqbufs(struct videobuf_queue *
goto done;
}
+ if (q->buf_type == VIDEOBUF_BUF_LINEAR) {
+ if (q->ops->buf_config) {
+ q->ops->buf_config(q, count);
+ } else {
+ dprintk(1, "reqbufs: buf_config cannot be NULL "
+ "when using linear buffer type\n");
+ retval = -EINVAL;
+ }
+ }
+
req->count = count;
done:
(at) (at) -1372,9 +1384,23 (at) (at) int videobuf_mmap_mapper(struct videobuf
map->start = vma->vm_start;
map->end = vma->vm_end;
map->q = q;
- vma->vm_ops = &videobuf_vm_ops;
- vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
- vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+ if (q->buf_type == VIDEOBUF_BUF_LINEAR) {
+#ifdef CONFIG_ARM
+ vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+#else
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+#endif
+ if (io_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
+ (vma->vm_end - vma->vm_start),
+ vma->vm_page_prot)) {
+ return -EAGAIN;
+ }
+ vma->vm_flags |= VM_RESERVED | VM_IO;
+ } else {
+ vma->vm_ops = &videobuf_vm_ops;
+ vma->vm_flags |= VM_DONTEXPAND | VM_RESERVED;
+ vma->vm_flags &= ~VM_IO; /* using shared anonymous pages */
+ }
vma->vm_private_data = map;
dprintk(1,"mmap %p: q=%p %08lx-%08lx pgoff %08lx bufs %d-%d\n",
map,q,vma->vm_start,vma->vm_end,vma->vm_pgoff,first,last);
Index: v4l-dvb/linux/drivers/media/video/vivi.c
===================================================================
Index: v4l-dvb/linux/include/media/video-buf.h
===================================================================
--- v4l-dvb.orig/linux/include/media/video-buf.h
+++ v4l-dvb/linux/include/media/video-buf.h
(at) (at) -4,10 +4,7 (at) (at)
* 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.
+ * The functions assume the driver does not need to touch the video data.
*
* device specific map/unmap/sync stuff now are mapped as file operations
* to allow its usage by USB and virtual devices.
(at) (at) -189,6 +186,8 (at) (at) struct videobuf_queue_ops {
struct videobuf_buffer *vb);
void (*buf_release)(struct videobuf_queue *q,
struct videobuf_buffer *vb);
+ void (*buf_config)(struct videobuf_queue *q,
+ unsigned int count);
/* Helper operations - device dependent.
* If null, videobuf_init defaults all to PCI handling
(at) (at) -199,6 +198,21 (at) (at) struct videobuf_queue_ops {
vb_map_sg_t *vb_unmap_sg;
};
+/*
+ * Define the buffer type to differenciate whether it is linear in
+ * physical memory. The memory allocation and mmap methods are
+ * different for these two type of buffers
+ *
+ * For linear buffers, they are allocated outside the videobuf module
+ * due to its large size, most likely in initialization time. For
+ * fragmented buffers, they are allocated page by page in the nopage
+ * function.
+ */
+enum videobuf_buf_type {
+ VIDEOBUF_BUF_LINEAR = 1,
+ VIDEOBUF_BUF_FRAGMENTED = 2,
+};
+
struct videobuf_queue {
#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15)
struct mutex lock;
(at) (at) -219,6 +233,7 (at) (at) struct videobuf_queue {
/* capture via mmap() + ioctl(QBUF/DQBUF) */
unsigned int streaming;
struct list_head stream;
+ enum videobuf_buf_type buf_type; /* linear vs. fragmented */
/* capture via read() */
unsigned int reading;
(at) (at) -244,6 +259,7 (at) (at) void videobuf_queue_init(struct videobuf
enum v4l2_buf_type type,
enum v4l2_field field,
unsigned int msize,
+ enum videobuf_buf_type buf_type,
void *priv);
int videobuf_queue_is_busy(struct videobuf_queue *q);
void videobuf_queue_cancel(struct videobuf_queue *q);
--- v4l-dvb.orig/linux/drivers/media/video/vivi.c
+++ v4l-dvb/linux/drivers/media/video/vivi.c
(at) (at) -69,6 +69,8 (at) (at) static unsigned int vid_limit = 16;
module_param(vid_limit,int,0644);
MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes");
+#define CONTIG_BUFFERS
+
/* supported controls */
static struct v4l2_queryctrl vivi_qctrl[] = {
{
(at) (at) -204,6 +206,12 (at) (at) struct vivi_fh {
struct videobuf_queue vb_vidq;
enum v4l2_buf_type type;
+
+#ifdef CONTIG_BUFFERS
+ u32 contig_buffers;
+ u32 contig_buf_size;
+ void *contig_buf[VIDEO_MAX_FRAME];
+#endif
};
/* ------------------------------------------------------------------
(at) (at) -244,6 +252,7 (at) (at) static u8 bars[8][3] = {
#define TSTAMP_MAX_Y TSTAMP_MIN_Y+15
#define TSTAMP_MIN_X 64
+#ifndef CONTIG_BUFFERS
static void prep_to_addr(struct sg_to_addr to_addr[],
struct videobuf_buffer *vb)
{
(at) (at) -276,19 +285,27 (at) (at) static int get_addr_pos(int pos, int pag
return (p1);
}
+#endif
static void gen_line(struct sg_to_addr to_addr[],int inipos,int pages,int wmax,
int hmax, int line, char *timestr)
{
- int w,i,j,pos=inipos,pgpos,oldpg,y;
+ int w,i,j,pos=inipos,y;
char *p,*s,*basep;
- struct page *pg;
u8 chr,r,g,b,color;
+#ifdef CONTIG_BUFFERS
+ /* interpret to_addr as plain pointer to contig buffer */
+ basep = (char *)to_addr;
+#else
+ int pgpos,oldpg;
+ struct page *pg;
+
/* Get first addr pointed to pixel position */
oldpg=get_addr_pos(pos,pages,to_addr);
pg=pfn_to_page(to_addr[oldpg].sg->dma_address >> PAGE_SHIFT);
basep = kmap_atomic(pg, KM_BOUNCE_READ)+to_addr[oldpg].sg->offset;
+#endif
/* We will just duplicate the second pixel at the packet */
wmax/=2;
(at) (at) -300,6 +317,9 (at) (at) static void gen_line(struct sg_to_addr t
b=bars[w*7/wmax][2];
for (color=0;color<4;color++) {
+#ifdef CONTIG_BUFFERS
+ p=basep+pos;
+#else
pgpos=get_addr_pos(pos,pages,to_addr);
if (pgpos!=oldpg) {
pg=pfn_to_page(to_addr[pgpos].sg->dma_address >> PAGE_SHIFT);
(at) (at) -308,6 +328,7 (at) (at) static void gen_line(struct sg_to_addr t
oldpg=pgpos;
}
p=basep+pos-to_addr[pgpos].pos;
+#endif
switch (color) {
case 0:
(at) (at) -352,6 +373,9 (at) (at) static void gen_line(struct sg_to_addr t
pos=inipos+j*2;
for (color=0;color<4;color++) {
+#ifdef CONTIG_BUFFERS
+ p=basep+pos;
+#else
pgpos=get_addr_pos(pos,pages,to_addr);
if (pgpos!=oldpg) {
pg=pfn_to_page(to_addr[pgpos].
(at) (at) -365,6 +389,7 (at) (at) static void gen_line(struct sg_to_addr t
oldpg=pgpos;
}
p=basep+pos-to_addr[pgpos].pos;
+#endif
y=TO_Y(r,g,b);
(at) (at) -412,6 +437,14 (at) (at) static void vivi_fillbuff(struct vivi_de
struct sg_to_addr *to_addr=buf->to_addr;
struct timeval ts;
+#ifdef CONTIG_BUFFERS
+ void *vaddr = phys_to_virt(vb->boff);
+
+ for (h=0;h<hmax;h++) {
+ gen_line(vaddr,pos,0,wmax,hmax,h,dev->timestr);
+ pos += wmax*2;
+ }
+#else
/* Test if DMA mapping is ready */
if (!vb->dma.sglist[0].dma_address)
return;
(at) (at) -425,6 +458,7 (at) (at) static void vivi_fillbuff(struct vivi_de
gen_line(to_addr,pos,vb->dma.nr_pages,wmax,hmax,h,dev->timestr);
pos += wmax*2;
}
+#endif
/* Updates stream time */
(at) (at) -727,6 +761,9 (at) (at) static int
buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
struct vivi_fh *fh = vq->priv_data;
+#ifdef CONTIG_BUFFERS
+ int i;
+#endif
*size = fh->width*fh->height*2;
(at) (at) -734,16 +771,88 (at) (at) buffer_setup(struct videobuf_queue *vq,
*count = 32;
while (*size * *count > vid_limit * 1024 * 1024)
(*count)--;
+
+#ifdef CONTIG_BUFFERS
+ dprintk(1, "%s: alloc contiguous buffer: size=%d (%d x %d)\n",
+ __FUNCTION__, (int)*size, (int)fh->width, (int)fh->height);
+
+ for(i=0; i<*count; i++) {
+ void *mem;
+ u32 order = get_order(*size);
+ u32 sz = PAGE_SIZE << order;
+
+ mem = (void *)__get_free_pages(GFP_KERNEL |GFP_DMA, order);
+ if (mem) {
+ unsigned long adr = (unsigned long)mem;
+
+ fh->contig_buf[i] = mem;
+ fh->contig_buffers++;
+ while (sz > 0) {
+ /* make sure the frame buffers are never
+ swapped out of memory */
+ SetPageReserved(virt_to_page(adr));
+ adr += PAGE_SIZE;
+ sz -= PAGE_SIZE;
+ }
+ } else {
+ panic("%s:%d unable to get free pages\n",
+ __FUNCTION__, __LINE__);
+ }
+ }
+ *count = fh->contig_buffers = i;
+ fh->contig_buf_size = *size;
+#endif
+
return 0;
}
+#ifdef CONTIG_BUFFERS
+static void
+buffer_config(struct videobuf_queue *q, unsigned int count)
+{
+ struct vivi_fh *fh = q->priv_data;
+ int i;
+
+ dprintk(1, "%s: count=%d\n",
+ __FUNCTION__, count);
+ for(i=0; i<count; i++) {
+ q->bufs[i]->boff = virt_to_phys(fh->contig_buf[i]);
+ dprintk(1, "buffer_config(): boff %d = 0x%08x\n",
+ i, q->bufs[i]->boff);
+ }
+}
+#endif
+
static void free_buffer(struct videobuf_queue *vq, struct vivi_buffer *buf)
{
+#ifdef CONTIG_BUFFERS
+ struct vivi_fh *fh = vq->priv_data;
+ int i;
+#endif
dprintk(1,"%s\n",__FUNCTION__);
if (in_interrupt())
BUG();
+#ifdef CONTIG_BUFFERS
+ for (i = 0; i < fh->contig_buffers; i++) {
+ u32 order = get_order(fh->contig_buf_size);
+ u32 sz = PAGE_SIZE << order;
+ u32 adr = (u32)fh->contig_buf[i];
+
+ while (sz > 0) {
+ ClearPageReserved(virt_to_page(adr));
+ adr += PAGE_SIZE;
+ sz -= PAGE_SIZE;
+ }
+ free_pages((u32)fh->contig_buf[i], order);
+ fh->contig_buf[i] = NULL;
+ }
+
+ fh->contig_buf_size = 0;
+ fh->contig_buffers = 0;
+#endif
+
/*FIXME: Maybe a spinlock is required here */
kfree(buf->to_addr);
buf->to_addr=NULL;
(at) (at) -785,10 +894,12 (at) (at) buffer_prepare(struct videobuf_queue *vq
init_buffer = 1;
}
+#ifndef CONTIG_BUFFERS
if (STATE_NEEDS_INIT == buf->vb.state) {
if (0 != (rc = videobuf_iolock(vq,&buf->vb,NULL)))
goto fail;
}
+#endif
buf->vb.state = STATE_PREPARED;
(at) (at) -861,6 +972,7 (at) (at) static void buffer_release(struct videob
free_buffer(vq,buf);
}
+#ifndef CONTIG_BUFFERS
static int vivi_map_sg(void *dev, struct scatterlist *sg, int nents,
int direction)
{
(at) (at) -893,17 +1005,22 (at) (at) static int vivi_dma_sync_sg(void *dev,st
// flush_write_buffers();
return 0;
}
+#endif
static struct videobuf_queue_ops vivi_video_qops = {
.buf_setup = buffer_setup,
.buf_prepare = buffer_prepare,
.buf_queue = buffer_queue,
.buf_release = buffer_release,
+#ifdef CONTIG_BUFFERS
+ .buf_config = buffer_config,
+#else
/* Non-pci handling routines */
.vb_map_sg = vivi_map_sg,
.vb_dma_sync_sg = vivi_dma_sync_sg,
.vb_unmap_sg = vivi_unmap_sg,
+#endif
};
/* ------------------------------------------------------------------
(at) (at) -1357,9 +1474,13 (at) (at) static int vivi_open(struct inode *inode
fh->dev = dev;
fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fh->fmt = &format;
+#ifdef CONTIG_BUFFERS
+ fh->width = 320;
+ fh->height = 240;
+#else
fh->width = 640;
fh->height = 480;
-
+#endif
/* Put all controls at a sane state */
for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
qctl_regs[i] =vivi_qctrl[i].default_value;
(at) (at) -1382,7 +1503,13 (at) (at) static int vivi_open(struct inode *inode
NULL, NULL,
fh->type,
V4L2_FIELD_INTERLACED,
- sizeof(struct vivi_buffer),fh);
+ sizeof(struct vivi_buffer),
+#ifdef CONTIG_BUFFERS
+ VIDEOBUF_BUF_LINEAR,
+#else
+ VIDEOBUF_BUF_FRAGMENTED,
+#endif
+ fh);
return 0;
}
(at) (at) -1412,7 +1539,12 (at) (at) vivi_poll(struct file *file, struct poll
if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
return POLLERR;
- if (res_get(fh->dev,fh)) {
+#ifdef CONTIG_BUFFERS
+ if (1)
+#else
+ if (res_get(fh->dev,fh))
+#endif
+ {
dprintk(1,"poll: mmap interface\n");
/* streaming capture */
if (list_empty(&fh->vb_vidq.stream))
--
video4linux-list mailing list
Unsubscribe mailto:video4linux-list-request (at) redhat.com?subject=unsubscribe
https://www.redhat.com/mailman/listinfo/video4linux-list