V4L/DVB (13348): cx23885: update model matrix for models 85021 and 85721
[safe/jmp/linux-2.6] / drivers / media / video / videobuf-dma-sg.c
index 717783f..a583d39 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * helper functions for SG DMA video4linux capture buffers
  *
- * The functions expect the hardware being able to scatter gatter
+ * The functions expect the hardware being able to scatter gather
  * (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.
@@ -21,6 +21,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
 
@@ -58,9 +59,10 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages)
        struct page *pg;
        int i;
 
-       sglist = kcalloc(nr_pages, sizeof(struct scatterlist), GFP_KERNEL);
+       sglist = vmalloc(nr_pages * sizeof(*sglist));
        if (NULL == sglist)
                return NULL;
+       memset(sglist, 0, nr_pages * sizeof(*sglist));
        sg_init_table(sglist, nr_pages);
        for (i = 0; i < nr_pages; i++, virt += PAGE_SIZE) {
                pg = vmalloc_to_page(virt);
@@ -72,7 +74,7 @@ videobuf_vmalloc_to_sg(unsigned char *virt, int nr_pages)
        return sglist;
 
  err:
-       kfree(sglist);
+       vfree(sglist);
        return NULL;
 }
 
@@ -80,17 +82,15 @@ struct scatterlist*
 videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset)
 {
        struct scatterlist *sglist;
-       int i = 0;
+       int i;
 
        if (NULL == pages[0])
                return NULL;
-       sglist = kcalloc(nr_pages, sizeof(*sglist), GFP_KERNEL);
+       sglist = vmalloc(nr_pages * sizeof(*sglist));
        if (NULL == sglist)
                return NULL;
        sg_init_table(sglist, nr_pages);
 
-       if (NULL == pages[0])
-               goto nopage;
        if (PageHighMem(pages[0]))
                /* DMA to highmem pages might not work */
                goto highmem;
@@ -106,12 +106,12 @@ videobuf_pages_to_sg(struct page **pages, int nr_pages, int offset)
 
  nopage:
        dprintk(2,"sgl: oops - no page\n");
-       kfree(sglist);
+       vfree(sglist);
        return NULL;
 
  highmem:
        dprintk(2,"sgl: oops - highmem page\n");
-       kfree(sglist);
+       vfree(sglist);
        return NULL;
 }
 
@@ -162,9 +162,6 @@ static int videobuf_dma_init_user_locked(struct videobuf_dmabuf *dma,
        dprintk(1,"init user [0x%lx+0x%lx => %d pages]\n",
                data,size,dma->nr_pages);
 
-       dma->varea = (void *) data;
-
-
        err = get_user_pages(current,current->mm,
                             data & PAGE_MASK, dma->nr_pages,
                             rw == READ, 1, /* force */
@@ -235,7 +232,7 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma)
                                                (dma->vmalloc,dma->nr_pages);
        }
        if (dma->bus_addr) {
-               dma->sglist = kmalloc(sizeof(struct scatterlist), GFP_KERNEL);
+               dma->sglist = vmalloc(sizeof(*dma->sglist));
                if (NULL != dma->sglist) {
                        dma->sglen  = 1;
                        sg_dma_address(&dma->sglist[0]) = dma->bus_addr & PAGE_MASK;
@@ -252,11 +249,11 @@ int videobuf_dma_map(struct videobuf_queue* q, struct videobuf_dmabuf *dma)
                                        dma->nr_pages, dma->direction);
                if (0 == dma->sglen) {
                        printk(KERN_WARNING
-                              "%s: videobuf_map_sg failed\n",__FUNCTION__);
-                       kfree(dma->sglist);
+                              "%s: videobuf_map_sg failed\n",__func__);
+                       vfree(dma->sglist);
                        dma->sglist = NULL;
                        dma->sglen = 0;
-                       return -EIO;
+                       return -ENOMEM;
                }
        }
        return 0;
@@ -279,7 +276,7 @@ int videobuf_dma_unmap(struct videobuf_queue* q,struct videobuf_dmabuf *dma)
 
        dma_unmap_sg(q->dev, dma->sglist, dma->nr_pages, dma->direction);
 
-       kfree(dma->sglist);
+       vfree(dma->sglist);
        dma->sglist = NULL;
        dma->sglen = 0;
        return 0;
@@ -300,7 +297,6 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma)
 
        vfree(dma->vmalloc);
        dma->vmalloc = NULL;
-       dma->varea = NULL;
 
        if (dma->bus_addr) {
                dma->bus_addr = 0;
@@ -394,13 +390,12 @@ videobuf_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
        page = alloc_page(GFP_USER | __GFP_DMA32);
        if (!page)
                return VM_FAULT_OOM;
-       clear_user_page(page_address(page), (unsigned long)vmf->virtual_address,
-                       page);
+       clear_user_highpage(page, (unsigned long)vmf->virtual_address);
        vmf->page = page;
        return 0;
 }
 
-static struct vm_operations_struct videobuf_vm_ops =
+static const struct vm_operations_struct videobuf_vm_ops =
 {
        .open     = videobuf_vm_open,
        .close    = videobuf_vm_close,
@@ -430,12 +425,22 @@ static void *__videobuf_alloc(size_t size)
        videobuf_dma_init(&mem->dma);
 
        dprintk(1,"%s: allocated at %p(%ld+%ld) & %p(%ld)\n",
-               __FUNCTION__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb),
+               __func__,vb,(long)sizeof(*vb),(long)size-sizeof(*vb),
                mem,(long)sizeof(*mem));
 
        return vb;
 }
 
+static void *__videobuf_to_vmalloc (struct videobuf_buffer *buf)
+{
+       struct videobuf_dma_sg_memory *mem = buf->priv;
+       BUG_ON(!mem);
+
+       MAGIC_CHECK(mem->magic, MAGIC_SG_MEM);
+
+       return mem->dma.vmalloc;
+}
+
 static int __videobuf_iolock (struct videobuf_queue* q,
                              struct videobuf_buffer *vb,
                              struct v4l2_framebuffer *fbuf)
@@ -546,6 +551,14 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
                goto done;
        }
 
+       /* This function maintains backwards compatibility with V4L1 and will
+        * map more than one buffer if the vma length is equal to the combined
+        * size of multiple buffers than it will map them together.  See
+        * VIDIOCGMBUF in the v4l spec
+        *
+        * TODO: Allow drivers to specify if they support this mode
+        */
+
        /* look for first buffer to map */
        for (first = 0; first < VIDEO_MAX_FRAME; first++) {
                if (NULL == q->bufs[first])
@@ -575,7 +588,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
                        retval = -EBUSY;
                        goto done;
                }
-               size += q->bufs[last]->bsize;
+               size += PAGE_ALIGN(q->bufs[last]->bsize);
                if (size == (vma->vm_end - vma->vm_start))
                        break;
        }
@@ -590,10 +603,16 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q,
        map = kmalloc(sizeof(struct videobuf_mapping),GFP_KERNEL);
        if (NULL == map)
                goto done;
-       for (size = 0, i = first; i <= last; size += q->bufs[i++]->bsize) {
+
+       size = 0;
+       for (i = first; i <= last; i++) {
+               if (NULL == q->bufs[i])
+                       continue;
                q->bufs[i]->map   = map;
                q->bufs[i]->baddr = vma->vm_start + size;
+               size += PAGE_ALIGN(q->bufs[i]->bsize);
        }
+
        map->count    = 1;
        map->start    = vma->vm_start;
        map->end      = vma->vm_end;
@@ -667,6 +686,7 @@ static struct videobuf_qtype_ops sg_ops = {
        .mmap_mapper  = __videobuf_mmap_mapper,
        .video_copy_to_user = __videobuf_copy_to_user,
        .copy_stream  = __videobuf_copy_stream,
+       .vmalloc      = __videobuf_to_vmalloc,
 };
 
 void *videobuf_sg_alloc(size_t size)