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