[media] v4l: videobuf2: add vmalloc allocator
[linux-2.6.git] / drivers / media / video / videobuf2-vmalloc.c
1 /*
2  * videobuf2-vmalloc.c - vmalloc memory allocator for videobuf2
3  *
4  * Copyright (C) 2010 Samsung Electronics
5  *
6  * Author: Pawel Osciak <p.osciak@samsung.com>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation.
11  */
12
13 #include <linux/module.h>
14 #include <linux/mm.h>
15 #include <linux/slab.h>
16 #include <linux/vmalloc.h>
17
18 #include <media/videobuf2-core.h>
19 #include <media/videobuf2-memops.h>
20
21 struct vb2_vmalloc_buf {
22         void                            *vaddr;
23         unsigned long                   size;
24         atomic_t                        refcount;
25         struct vb2_vmarea_handler       handler;
26 };
27
28 static void vb2_vmalloc_put(void *buf_priv);
29
30 static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
31 {
32         struct vb2_vmalloc_buf *buf;
33
34         buf = kzalloc(sizeof *buf, GFP_KERNEL);
35         if (!buf)
36                 return NULL;
37
38         buf->size = size;
39         buf->vaddr = vmalloc_user(buf->size);
40         buf->handler.refcount = &buf->refcount;
41         buf->handler.put = vb2_vmalloc_put;
42         buf->handler.arg = buf;
43
44         if (!buf->vaddr) {
45                 printk(KERN_ERR "vmalloc of size %ld failed\n", buf->size);
46                 kfree(buf);
47                 return NULL;
48         }
49
50         atomic_inc(&buf->refcount);
51         printk(KERN_DEBUG "Allocated vmalloc buffer of size %ld at vaddr=%p\n",
52                         buf->size, buf->vaddr);
53
54         return buf;
55 }
56
57 static void vb2_vmalloc_put(void *buf_priv)
58 {
59         struct vb2_vmalloc_buf *buf = buf_priv;
60
61         if (atomic_dec_and_test(&buf->refcount)) {
62                 printk(KERN_DEBUG "%s: Freeing vmalloc mem at vaddr=%p\n",
63                         __func__, buf->vaddr);
64                 vfree(buf->vaddr);
65                 kfree(buf);
66         }
67 }
68
69 static void *vb2_vmalloc_vaddr(void *buf_priv)
70 {
71         struct vb2_vmalloc_buf *buf = buf_priv;
72
73         BUG_ON(!buf);
74
75         if (!buf->vaddr) {
76                 printk(KERN_ERR "Address of an unallocated plane requested\n");
77                 return NULL;
78         }
79
80         return buf->vaddr;
81 }
82
83 static unsigned int vb2_vmalloc_num_users(void *buf_priv)
84 {
85         struct vb2_vmalloc_buf *buf = buf_priv;
86         return atomic_read(&buf->refcount);
87 }
88
89 static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
90 {
91         struct vb2_vmalloc_buf *buf = buf_priv;
92         int ret;
93
94         if (!buf) {
95                 printk(KERN_ERR "No memory to map\n");
96                 return -EINVAL;
97         }
98
99         ret = remap_vmalloc_range(vma, buf->vaddr, 0);
100         if (ret) {
101                 printk(KERN_ERR "Remapping vmalloc memory, error: %d\n", ret);
102                 return ret;
103         }
104
105         /*
106          * Make sure that vm_areas for 2 buffers won't be merged together
107          */
108         vma->vm_flags           |= VM_DONTEXPAND;
109
110         /*
111          * Use common vm_area operations to track buffer refcount.
112          */
113         vma->vm_private_data    = &buf->handler;
114         vma->vm_ops             = &vb2_common_vm_ops;
115
116         vma->vm_ops->open(vma);
117
118         return 0;
119 }
120
121 const struct vb2_mem_ops vb2_vmalloc_memops = {
122         .alloc          = vb2_vmalloc_alloc,
123         .put            = vb2_vmalloc_put,
124         .vaddr          = vb2_vmalloc_vaddr,
125         .mmap           = vb2_vmalloc_mmap,
126         .num_users      = vb2_vmalloc_num_users,
127 };
128 EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
129
130 MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
131 MODULE_AUTHOR("Pawel Osciak");
132 MODULE_LICENSE("GPL");