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
);
82 __free_page(buf
->pages
[i
]);
85 fail_pages_array_alloc
:
86 vfree(buf
->sg_desc
.sglist
);
93 static void vb2_dma_sg_put(void *buf_priv
)
95 struct vb2_dma_sg_buf
*buf
= buf_priv
;
96 int i
= buf
->sg_desc
.num_pages
;
98 if (atomic_dec_and_test(&buf
->refcount
)) {
99 printk(KERN_DEBUG
"%s: Freeing buffer of %d pages\n", __func__
,
100 buf
->sg_desc
.num_pages
);
102 vm_unmap_ram(buf
->vaddr
, buf
->sg_desc
.num_pages
);
103 vfree(buf
->sg_desc
.sglist
);
105 __free_page(buf
->pages
[i
]);
111 static void *vb2_dma_sg_get_userptr(void *alloc_ctx
, unsigned long vaddr
,
112 unsigned long size
, int write
)
114 struct vb2_dma_sg_buf
*buf
;
115 unsigned long first
, last
;
116 int num_pages_from_user
, i
;
118 buf
= kzalloc(sizeof *buf
, GFP_KERNEL
);
124 buf
->offset
= vaddr
& ~PAGE_MASK
;
125 buf
->sg_desc
.size
= size
;
127 first
= (vaddr
& PAGE_MASK
) >> PAGE_SHIFT
;
128 last
= ((vaddr
+ size
- 1) & PAGE_MASK
) >> PAGE_SHIFT
;
129 buf
->sg_desc
.num_pages
= last
- first
+ 1;
131 buf
->sg_desc
.sglist
= vzalloc(
132 buf
->sg_desc
.num_pages
* sizeof(*buf
->sg_desc
.sglist
));
133 if (!buf
->sg_desc
.sglist
)
134 goto userptr_fail_sglist_alloc
;
136 sg_init_table(buf
->sg_desc
.sglist
, buf
->sg_desc
.num_pages
);
138 buf
->pages
= kzalloc(buf
->sg_desc
.num_pages
* sizeof(struct page
*),
141 goto userptr_fail_pages_array_alloc
;
143 num_pages_from_user
= get_user_pages(current
, current
->mm
,
145 buf
->sg_desc
.num_pages
,
151 if (num_pages_from_user
!= buf
->sg_desc
.num_pages
)
152 goto userptr_fail_get_user_pages
;
154 sg_set_page(&buf
->sg_desc
.sglist
[0], buf
->pages
[0],
155 PAGE_SIZE
- buf
->offset
, buf
->offset
);
156 size
-= PAGE_SIZE
- buf
->offset
;
157 for (i
= 1; i
< buf
->sg_desc
.num_pages
; ++i
) {
158 sg_set_page(&buf
->sg_desc
.sglist
[i
], buf
->pages
[i
],
159 min_t(size_t, PAGE_SIZE
, size
), 0);
160 size
-= min_t(size_t, PAGE_SIZE
, size
);
164 userptr_fail_get_user_pages
:
165 printk(KERN_DEBUG
"get_user_pages requested/got: %d/%d]\n",
166 num_pages_from_user
, buf
->sg_desc
.num_pages
);
167 while (--num_pages_from_user
>= 0)
168 put_page(buf
->pages
[num_pages_from_user
]);
171 userptr_fail_pages_array_alloc
:
172 vfree(buf
->sg_desc
.sglist
);
174 userptr_fail_sglist_alloc
:
180 * @put_userptr: inform the allocator that a USERPTR buffer will no longer
183 static void vb2_dma_sg_put_userptr(void *buf_priv
)
185 struct vb2_dma_sg_buf
*buf
= buf_priv
;
186 int i
= buf
->sg_desc
.num_pages
;
188 printk(KERN_DEBUG
"%s: Releasing userspace buffer of %d pages\n",
189 __func__
, buf
->sg_desc
.num_pages
);
191 vm_unmap_ram(buf
->vaddr
, buf
->sg_desc
.num_pages
);
194 set_page_dirty_lock(buf
->pages
[i
]);
195 put_page(buf
->pages
[i
]);
197 vfree(buf
->sg_desc
.sglist
);
202 static void *vb2_dma_sg_vaddr(void *buf_priv
)
204 struct vb2_dma_sg_buf
*buf
= buf_priv
;
209 buf
->vaddr
= vm_map_ram(buf
->pages
,
210 buf
->sg_desc
.num_pages
,
214 /* add offset in case userptr is not page-aligned */
215 return buf
->vaddr
+ buf
->offset
;
218 static unsigned int vb2_dma_sg_num_users(void *buf_priv
)
220 struct vb2_dma_sg_buf
*buf
= buf_priv
;
222 return atomic_read(&buf
->refcount
);
225 static int vb2_dma_sg_mmap(void *buf_priv
, struct vm_area_struct
*vma
)
227 struct vb2_dma_sg_buf
*buf
= buf_priv
;
228 unsigned long uaddr
= vma
->vm_start
;
229 unsigned long usize
= vma
->vm_end
- vma
->vm_start
;
233 printk(KERN_ERR
"No memory to map\n");
240 ret
= vm_insert_page(vma
, uaddr
, buf
->pages
[i
++]);
242 printk(KERN_ERR
"Remapping memory, error: %d\n", ret
);
252 * Use common vm_area operations to track buffer refcount.
254 vma
->vm_private_data
= &buf
->handler
;
255 vma
->vm_ops
= &vb2_common_vm_ops
;
257 vma
->vm_ops
->open(vma
);
262 static void *vb2_dma_sg_cookie(void *buf_priv
)
264 struct vb2_dma_sg_buf
*buf
= buf_priv
;
266 return &buf
->sg_desc
;
269 const struct vb2_mem_ops vb2_dma_sg_memops
= {
270 .alloc
= vb2_dma_sg_alloc
,
271 .put
= vb2_dma_sg_put
,
272 .get_userptr
= vb2_dma_sg_get_userptr
,
273 .put_userptr
= vb2_dma_sg_put_userptr
,
274 .vaddr
= vb2_dma_sg_vaddr
,
275 .mmap
= vb2_dma_sg_mmap
,
276 .num_users
= vb2_dma_sg_num_users
,
277 .cookie
= vb2_dma_sg_cookie
,
279 EXPORT_SYMBOL_GPL(vb2_dma_sg_memops
);
281 MODULE_DESCRIPTION("dma scatter/gather memory handling routines for videobuf2");
282 MODULE_AUTHOR("Andrzej Pietrasiewicz");
283 MODULE_LICENSE("GPL");