2 * videobuf2-dma-sg.c - dma scatter/gather memory allocator for videobuf2
4 * Copyright (C) 2010 Samsung Electronics
6 * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
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.
13 #include <linux/module.h>
15 #include <linux/scatterlist.h>
16 #include <linux/sched.h>
17 #include <linux/slab.h>
18 #include <linux/vmalloc.h>
20 #include <media/videobuf2-core.h>
21 #include <media/videobuf2-memops.h>
22 #include <media/videobuf2-dma-sg.h>
24 struct vb2_dma_sg_buf
{
29 struct vb2_dma_sg_desc sg_desc
;
31 struct vb2_vmarea_handler handler
;
34 static void vb2_dma_sg_put(void *buf_priv
);
36 static void *vb2_dma_sg_alloc(void *alloc_ctx
, unsigned long size
)
38 struct vb2_dma_sg_buf
*buf
;
41 buf
= kzalloc(sizeof *buf
, GFP_KERNEL
);
48 buf
->sg_desc
.size
= size
;
49 buf
->sg_desc
.num_pages
= (size
+ PAGE_SIZE
- 1) >> PAGE_SHIFT
;
51 buf
->sg_desc
.sglist
= vzalloc(buf
->sg_desc
.num_pages
*
52 sizeof(*buf
->sg_desc
.sglist
));
53 if (!buf
->sg_desc
.sglist
)
54 goto fail_sglist_alloc
;
55 sg_init_table(buf
->sg_desc
.sglist
, buf
->sg_desc
.num_pages
);
57 buf
->pages
= kzalloc(buf
->sg_desc
.num_pages
* sizeof(struct page
*),
60 goto fail_pages_array_alloc
;
62 for (i
= 0; i
< buf
->sg_desc
.num_pages
; ++i
) {
63 buf
->pages
[i
] = alloc_page(GFP_KERNEL
| __GFP_ZERO
| __GFP_NOWARN
);
64 if (NULL
== buf
->pages
[i
])
65 goto fail_pages_alloc
;
66 sg_set_page(&buf
->sg_desc
.sglist
[i
],
67 buf
->pages
[i
], PAGE_SIZE
, 0);
70 buf
->handler
.refcount
= &buf
->refcount
;
71 buf
->handler
.put
= vb2_dma_sg_put
;
72 buf
->handler
.arg
= buf
;
74 atomic_inc(&buf
->refcount
);
76 printk(KERN_DEBUG
"%s: Allocated buffer of %d pages\n",
77 __func__
, buf
->sg_desc
.num_pages
);
80 buf
->vaddr
= vm_map_ram(buf
->pages
,
81 buf
->sg_desc
.num_pages
,
88 __free_page(buf
->pages
[i
]);
91 fail_pages_array_alloc
:
92 vfree(buf
->sg_desc
.sglist
);
99 static void vb2_dma_sg_put(void *buf_priv
)
101 struct vb2_dma_sg_buf
*buf
= buf_priv
;
102 int i
= buf
->sg_desc
.num_pages
;
104 if (atomic_dec_and_test(&buf
->refcount
)) {
105 printk(KERN_DEBUG
"%s: Freeing buffer of %d pages\n", __func__
,
106 buf
->sg_desc
.num_pages
);
108 vm_unmap_ram(buf
->vaddr
, buf
->sg_desc
.num_pages
);
109 vfree(buf
->sg_desc
.sglist
);
111 __free_page(buf
->pages
[i
]);
117 static void *vb2_dma_sg_get_userptr(void *alloc_ctx
, unsigned long vaddr
,
118 unsigned long size
, int write
)
120 struct vb2_dma_sg_buf
*buf
;
121 unsigned long first
, last
;
122 int num_pages_from_user
, i
;
124 buf
= kzalloc(sizeof *buf
, GFP_KERNEL
);
130 buf
->offset
= vaddr
& ~PAGE_MASK
;
131 buf
->sg_desc
.size
= size
;
133 first
= (vaddr
& PAGE_MASK
) >> PAGE_SHIFT
;
134 last
= ((vaddr
+ size
- 1) & PAGE_MASK
) >> PAGE_SHIFT
;
135 buf
->sg_desc
.num_pages
= last
- first
+ 1;
137 buf
->sg_desc
.sglist
= vzalloc(
138 buf
->sg_desc
.num_pages
* sizeof(*buf
->sg_desc
.sglist
));
139 if (!buf
->sg_desc
.sglist
)
140 goto userptr_fail_sglist_alloc
;
142 sg_init_table(buf
->sg_desc
.sglist
, buf
->sg_desc
.num_pages
);
144 buf
->pages
= kzalloc(buf
->sg_desc
.num_pages
* sizeof(struct page
*),
147 goto userptr_fail_pages_array_alloc
;
149 down_read(¤t
->mm
->mmap_sem
);
150 num_pages_from_user
= get_user_pages(current
, current
->mm
,
152 buf
->sg_desc
.num_pages
,
157 up_read(¤t
->mm
->mmap_sem
);
158 if (num_pages_from_user
!= buf
->sg_desc
.num_pages
)
159 goto userptr_fail_get_user_pages
;
161 sg_set_page(&buf
->sg_desc
.sglist
[0], buf
->pages
[0],
162 PAGE_SIZE
- buf
->offset
, buf
->offset
);
163 size
-= PAGE_SIZE
- buf
->offset
;
164 for (i
= 1; i
< buf
->sg_desc
.num_pages
; ++i
) {
165 sg_set_page(&buf
->sg_desc
.sglist
[i
], buf
->pages
[i
],
166 min_t(size_t, PAGE_SIZE
, size
), 0);
167 size
-= min_t(size_t, PAGE_SIZE
, size
);
171 userptr_fail_get_user_pages
:
172 printk(KERN_DEBUG
"get_user_pages requested/got: %d/%d]\n",
173 num_pages_from_user
, buf
->sg_desc
.num_pages
);
174 while (--num_pages_from_user
>= 0)
175 put_page(buf
->pages
[num_pages_from_user
]);
178 userptr_fail_pages_array_alloc
:
179 vfree(buf
->sg_desc
.sglist
);
181 userptr_fail_sglist_alloc
:
187 * @put_userptr: inform the allocator that a USERPTR buffer will no longer
190 static void vb2_dma_sg_put_userptr(void *buf_priv
)
192 struct vb2_dma_sg_buf
*buf
= buf_priv
;
193 int i
= buf
->sg_desc
.num_pages
;
195 printk(KERN_DEBUG
"%s: Releasing userspace buffer of %d pages\n",
196 __func__
, buf
->sg_desc
.num_pages
);
198 vm_unmap_ram(buf
->vaddr
, buf
->sg_desc
.num_pages
);
201 set_page_dirty_lock(buf
->pages
[i
]);
202 put_page(buf
->pages
[i
]);
204 vfree(buf
->sg_desc
.sglist
);
209 static void *vb2_dma_sg_vaddr(void *buf_priv
)
211 struct vb2_dma_sg_buf
*buf
= buf_priv
;
216 buf
->vaddr
= vm_map_ram(buf
->pages
,
217 buf
->sg_desc
.num_pages
,
221 /* add offset in case userptr is not page-aligned */
222 return buf
->vaddr
+ buf
->offset
;
225 static unsigned int vb2_dma_sg_num_users(void *buf_priv
)
227 struct vb2_dma_sg_buf
*buf
= buf_priv
;
229 return atomic_read(&buf
->refcount
);
232 static int vb2_dma_sg_mmap(void *buf_priv
, struct vm_area_struct
*vma
)
234 struct vb2_dma_sg_buf
*buf
= buf_priv
;
235 unsigned long uaddr
= vma
->vm_start
;
236 unsigned long usize
= vma
->vm_end
- vma
->vm_start
;
240 printk(KERN_ERR
"No memory to map\n");
247 ret
= vm_insert_page(vma
, uaddr
, buf
->pages
[i
++]);
249 printk(KERN_ERR
"Remapping memory, error: %d\n", ret
);
259 * Use common vm_area operations to track buffer refcount.
261 vma
->vm_private_data
= &buf
->handler
;
262 vma
->vm_ops
= &vb2_common_vm_ops
;
264 vma
->vm_ops
->open(vma
);
269 static void *vb2_dma_sg_cookie(void *buf_priv
)
271 struct vb2_dma_sg_buf
*buf
= buf_priv
;
273 return &buf
->sg_desc
;
276 const struct vb2_mem_ops vb2_dma_sg_memops
= {
277 .alloc
= vb2_dma_sg_alloc
,
278 .put
= vb2_dma_sg_put
,
279 .get_userptr
= vb2_dma_sg_get_userptr
,
280 .put_userptr
= vb2_dma_sg_put_userptr
,
281 .vaddr
= vb2_dma_sg_vaddr
,
282 .mmap
= vb2_dma_sg_mmap
,
283 .num_users
= vb2_dma_sg_num_users
,
284 .cookie
= vb2_dma_sg_cookie
,
286 EXPORT_SYMBOL_GPL(vb2_dma_sg_memops
);
288 MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
289 MODULE_AUTHOR("Andrzej Pietrasiewicz");
290 MODULE_LICENSE("GPL");