Merge tag 'for-linus-3.4-merge-window' of git://git.kernel.org/pub/scm/linux/kernel...
[platform/adaptation/renesas_rcar/renesas_kernel.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 <pawel@osciak.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/io.h>
14 #include <linux/module.h>
15 #include <linux/mm.h>
16 #include <linux/sched.h>
17 #include <linux/slab.h>
18 #include <linux/vmalloc.h>
19
20 #include <media/videobuf2-core.h>
21 #include <media/videobuf2-memops.h>
22
23 struct vb2_vmalloc_buf {
24         void                            *vaddr;
25         struct page                     **pages;
26         struct vm_area_struct           *vma;
27         int                             write;
28         unsigned long                   size;
29         unsigned int                    n_pages;
30         atomic_t                        refcount;
31         struct vb2_vmarea_handler       handler;
32 };
33
34 static void vb2_vmalloc_put(void *buf_priv);
35
36 static void *vb2_vmalloc_alloc(void *alloc_ctx, unsigned long size)
37 {
38         struct vb2_vmalloc_buf *buf;
39
40         buf = kzalloc(sizeof(*buf), GFP_KERNEL);
41         if (!buf)
42                 return NULL;
43
44         buf->size = size;
45         buf->vaddr = vmalloc_user(buf->size);
46         buf->handler.refcount = &buf->refcount;
47         buf->handler.put = vb2_vmalloc_put;
48         buf->handler.arg = buf;
49
50         if (!buf->vaddr) {
51                 pr_debug("vmalloc of size %ld failed\n", buf->size);
52                 kfree(buf);
53                 return NULL;
54         }
55
56         atomic_inc(&buf->refcount);
57         return buf;
58 }
59
60 static void vb2_vmalloc_put(void *buf_priv)
61 {
62         struct vb2_vmalloc_buf *buf = buf_priv;
63
64         if (atomic_dec_and_test(&buf->refcount)) {
65                 vfree(buf->vaddr);
66                 kfree(buf);
67         }
68 }
69
70 static void *vb2_vmalloc_get_userptr(void *alloc_ctx, unsigned long vaddr,
71                                      unsigned long size, int write)
72 {
73         struct vb2_vmalloc_buf *buf;
74         unsigned long first, last;
75         int n_pages, offset;
76         struct vm_area_struct *vma;
77         dma_addr_t physp;
78
79         buf = kzalloc(sizeof(*buf), GFP_KERNEL);
80         if (!buf)
81                 return NULL;
82
83         buf->write = write;
84         offset = vaddr & ~PAGE_MASK;
85         buf->size = size;
86
87
88         vma = find_vma(current->mm, vaddr);
89         if (vma && (vma->vm_flags & VM_PFNMAP) && (vma->vm_pgoff)) {
90                 if (vb2_get_contig_userptr(vaddr, size, &vma, &physp))
91                         goto fail_pages_array_alloc;
92                 buf->vma = vma;
93                 buf->vaddr = ioremap_nocache(physp, size);
94                 if (!buf->vaddr)
95                         goto fail_pages_array_alloc;
96         } else {
97                 first = vaddr >> PAGE_SHIFT;
98                 last  = (vaddr + size - 1) >> PAGE_SHIFT;
99                 buf->n_pages = last - first + 1;
100                 buf->pages = kzalloc(buf->n_pages * sizeof(struct page *),
101                                      GFP_KERNEL);
102                 if (!buf->pages)
103                         goto fail_pages_array_alloc;
104
105                 /* current->mm->mmap_sem is taken by videobuf2 core */
106                 n_pages = get_user_pages(current, current->mm,
107                                          vaddr & PAGE_MASK, buf->n_pages,
108                                          write, 1, /* force */
109                                          buf->pages, NULL);
110                 if (n_pages != buf->n_pages)
111                         goto fail_get_user_pages;
112
113                 buf->vaddr = vm_map_ram(buf->pages, buf->n_pages, -1,
114                                         PAGE_KERNEL);
115                 if (!buf->vaddr)
116                         goto fail_get_user_pages;
117         }
118
119         buf->vaddr += offset;
120         return buf;
121
122 fail_get_user_pages:
123         pr_debug("get_user_pages requested/got: %d/%d]\n", n_pages,
124                  buf->n_pages);
125         while (--n_pages >= 0)
126                 put_page(buf->pages[n_pages]);
127         kfree(buf->pages);
128
129 fail_pages_array_alloc:
130         kfree(buf);
131
132         return NULL;
133 }
134
135 static void vb2_vmalloc_put_userptr(void *buf_priv)
136 {
137         struct vb2_vmalloc_buf *buf = buf_priv;
138         unsigned long vaddr = (unsigned long)buf->vaddr & PAGE_MASK;
139         unsigned int i;
140
141         if (buf->pages) {
142                 if (vaddr)
143                         vm_unmap_ram((void *)vaddr, buf->n_pages);
144                 for (i = 0; i < buf->n_pages; ++i) {
145                         if (buf->write)
146                                 set_page_dirty_lock(buf->pages[i]);
147                         put_page(buf->pages[i]);
148                 }
149                 kfree(buf->pages);
150         } else {
151                 if (buf->vma)
152                         vb2_put_vma(buf->vma);
153                 iounmap(buf->vaddr);
154         }
155         kfree(buf);
156 }
157
158 static void *vb2_vmalloc_vaddr(void *buf_priv)
159 {
160         struct vb2_vmalloc_buf *buf = buf_priv;
161
162         if (!buf->vaddr) {
163                 pr_err("Address of an unallocated plane requested "
164                        "or cannot map user pointer\n");
165                 return NULL;
166         }
167
168         return buf->vaddr;
169 }
170
171 static unsigned int vb2_vmalloc_num_users(void *buf_priv)
172 {
173         struct vb2_vmalloc_buf *buf = buf_priv;
174         return atomic_read(&buf->refcount);
175 }
176
177 static int vb2_vmalloc_mmap(void *buf_priv, struct vm_area_struct *vma)
178 {
179         struct vb2_vmalloc_buf *buf = buf_priv;
180         int ret;
181
182         if (!buf) {
183                 pr_err("No memory to map\n");
184                 return -EINVAL;
185         }
186
187         ret = remap_vmalloc_range(vma, buf->vaddr, 0);
188         if (ret) {
189                 pr_err("Remapping vmalloc memory, error: %d\n", ret);
190                 return ret;
191         }
192
193         /*
194          * Make sure that vm_areas for 2 buffers won't be merged together
195          */
196         vma->vm_flags           |= VM_DONTEXPAND;
197
198         /*
199          * Use common vm_area operations to track buffer refcount.
200          */
201         vma->vm_private_data    = &buf->handler;
202         vma->vm_ops             = &vb2_common_vm_ops;
203
204         vma->vm_ops->open(vma);
205
206         return 0;
207 }
208
209 const struct vb2_mem_ops vb2_vmalloc_memops = {
210         .alloc          = vb2_vmalloc_alloc,
211         .put            = vb2_vmalloc_put,
212         .get_userptr    = vb2_vmalloc_get_userptr,
213         .put_userptr    = vb2_vmalloc_put_userptr,
214         .vaddr          = vb2_vmalloc_vaddr,
215         .mmap           = vb2_vmalloc_mmap,
216         .num_users      = vb2_vmalloc_num_users,
217 };
218 EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
219
220 MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
221 MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>");
222 MODULE_LICENSE("GPL");