blob: b5e6936fb628028701c1f5139b47a0b0826140b6 [file] [log] [blame]
Pawel Osciak3c18ff02010-10-11 10:58:53 -03001/*
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
21struct vb2_vmalloc_buf {
22 void *vaddr;
23 unsigned long size;
24 atomic_t refcount;
25 struct vb2_vmarea_handler handler;
26};
27
28static void vb2_vmalloc_put(void *buf_priv);
29
30static 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
57static 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
69static 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
83static 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
89static 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
121const 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};
128EXPORT_SYMBOL_GPL(vb2_vmalloc_memops);
129
130MODULE_DESCRIPTION("vmalloc memory handling routines for videobuf2");
131MODULE_AUTHOR("Pawel Osciak");
132MODULE_LICENSE("GPL");