Commit | Line | Data |
---|---|---|
1a758d4e PO |
1 | /* |
2 | * videobuf2-dma-contig.c - DMA contig memory allocator for videobuf2 | |
3 | * | |
4 | * Copyright (C) 2010 Samsung Electronics | |
5 | * | |
95072084 | 6 | * Author: Pawel Osciak <pawel@osciak.com> |
1a758d4e PO |
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/slab.h> | |
15 | #include <linux/dma-mapping.h> | |
16 | ||
17 | #include <media/videobuf2-core.h> | |
d0df3c38 | 18 | #include <media/videobuf2-dma-contig.h> |
1a758d4e PO |
19 | #include <media/videobuf2-memops.h> |
20 | ||
21 | struct vb2_dc_conf { | |
22 | struct device *dev; | |
23 | }; | |
24 | ||
25 | struct vb2_dc_buf { | |
26 | struct vb2_dc_conf *conf; | |
27 | void *vaddr; | |
ba7fcb0c | 28 | dma_addr_t dma_addr; |
1a758d4e PO |
29 | unsigned long size; |
30 | struct vm_area_struct *vma; | |
31 | atomic_t refcount; | |
32 | struct vb2_vmarea_handler handler; | |
33 | }; | |
34 | ||
35 | static void vb2_dma_contig_put(void *buf_priv); | |
36 | ||
37 | static void *vb2_dma_contig_alloc(void *alloc_ctx, unsigned long size) | |
38 | { | |
39 | struct vb2_dc_conf *conf = alloc_ctx; | |
40 | struct vb2_dc_buf *buf; | |
41 | ||
42 | buf = kzalloc(sizeof *buf, GFP_KERNEL); | |
43 | if (!buf) | |
44 | return ERR_PTR(-ENOMEM); | |
45 | ||
ba7fcb0c | 46 | buf->vaddr = dma_alloc_coherent(conf->dev, size, &buf->dma_addr, |
1a758d4e PO |
47 | GFP_KERNEL); |
48 | if (!buf->vaddr) { | |
49 | dev_err(conf->dev, "dma_alloc_coherent of size %ld failed\n", | |
99a41771 | 50 | size); |
1a758d4e PO |
51 | kfree(buf); |
52 | return ERR_PTR(-ENOMEM); | |
53 | } | |
54 | ||
55 | buf->conf = conf; | |
56 | buf->size = size; | |
57 | ||
58 | buf->handler.refcount = &buf->refcount; | |
59 | buf->handler.put = vb2_dma_contig_put; | |
60 | buf->handler.arg = buf; | |
61 | ||
62 | atomic_inc(&buf->refcount); | |
63 | ||
64 | return buf; | |
65 | } | |
66 | ||
67 | static void vb2_dma_contig_put(void *buf_priv) | |
68 | { | |
69 | struct vb2_dc_buf *buf = buf_priv; | |
70 | ||
71 | if (atomic_dec_and_test(&buf->refcount)) { | |
72 | dma_free_coherent(buf->conf->dev, buf->size, buf->vaddr, | |
ba7fcb0c | 73 | buf->dma_addr); |
1a758d4e PO |
74 | kfree(buf); |
75 | } | |
76 | } | |
77 | ||
78 | static void *vb2_dma_contig_cookie(void *buf_priv) | |
79 | { | |
80 | struct vb2_dc_buf *buf = buf_priv; | |
81 | ||
ba7fcb0c | 82 | return &buf->dma_addr; |
1a758d4e PO |
83 | } |
84 | ||
85 | static void *vb2_dma_contig_vaddr(void *buf_priv) | |
86 | { | |
87 | struct vb2_dc_buf *buf = buf_priv; | |
88 | if (!buf) | |
121b3ddb | 89 | return NULL; |
1a758d4e PO |
90 | |
91 | return buf->vaddr; | |
92 | } | |
93 | ||
94 | static unsigned int vb2_dma_contig_num_users(void *buf_priv) | |
95 | { | |
96 | struct vb2_dc_buf *buf = buf_priv; | |
97 | ||
98 | return atomic_read(&buf->refcount); | |
99 | } | |
100 | ||
101 | static int vb2_dma_contig_mmap(void *buf_priv, struct vm_area_struct *vma) | |
102 | { | |
103 | struct vb2_dc_buf *buf = buf_priv; | |
104 | ||
105 | if (!buf) { | |
106 | printk(KERN_ERR "No buffer to map\n"); | |
107 | return -EINVAL; | |
108 | } | |
109 | ||
ba7fcb0c | 110 | return vb2_mmap_pfn_range(vma, buf->dma_addr, buf->size, |
1a758d4e PO |
111 | &vb2_common_vm_ops, &buf->handler); |
112 | } | |
113 | ||
114 | static void *vb2_dma_contig_get_userptr(void *alloc_ctx, unsigned long vaddr, | |
115 | unsigned long size, int write) | |
116 | { | |
117 | struct vb2_dc_buf *buf; | |
118 | struct vm_area_struct *vma; | |
ba7fcb0c | 119 | dma_addr_t dma_addr = 0; |
1a758d4e PO |
120 | int ret; |
121 | ||
122 | buf = kzalloc(sizeof *buf, GFP_KERNEL); | |
123 | if (!buf) | |
124 | return ERR_PTR(-ENOMEM); | |
125 | ||
ba7fcb0c | 126 | ret = vb2_get_contig_userptr(vaddr, size, &vma, &dma_addr); |
1a758d4e PO |
127 | if (ret) { |
128 | printk(KERN_ERR "Failed acquiring VMA for vaddr 0x%08lx\n", | |
129 | vaddr); | |
130 | kfree(buf); | |
131 | return ERR_PTR(ret); | |
132 | } | |
133 | ||
134 | buf->size = size; | |
ba7fcb0c | 135 | buf->dma_addr = dma_addr; |
1a758d4e PO |
136 | buf->vma = vma; |
137 | ||
138 | return buf; | |
139 | } | |
140 | ||
141 | static void vb2_dma_contig_put_userptr(void *mem_priv) | |
142 | { | |
143 | struct vb2_dc_buf *buf = mem_priv; | |
144 | ||
145 | if (!buf) | |
146 | return; | |
147 | ||
148 | vb2_put_vma(buf->vma); | |
149 | kfree(buf); | |
150 | } | |
151 | ||
152 | const struct vb2_mem_ops vb2_dma_contig_memops = { | |
153 | .alloc = vb2_dma_contig_alloc, | |
154 | .put = vb2_dma_contig_put, | |
155 | .cookie = vb2_dma_contig_cookie, | |
156 | .vaddr = vb2_dma_contig_vaddr, | |
157 | .mmap = vb2_dma_contig_mmap, | |
158 | .get_userptr = vb2_dma_contig_get_userptr, | |
159 | .put_userptr = vb2_dma_contig_put_userptr, | |
160 | .num_users = vb2_dma_contig_num_users, | |
161 | }; | |
162 | EXPORT_SYMBOL_GPL(vb2_dma_contig_memops); | |
163 | ||
164 | void *vb2_dma_contig_init_ctx(struct device *dev) | |
165 | { | |
166 | struct vb2_dc_conf *conf; | |
167 | ||
168 | conf = kzalloc(sizeof *conf, GFP_KERNEL); | |
169 | if (!conf) | |
170 | return ERR_PTR(-ENOMEM); | |
171 | ||
172 | conf->dev = dev; | |
173 | ||
174 | return conf; | |
175 | } | |
176 | EXPORT_SYMBOL_GPL(vb2_dma_contig_init_ctx); | |
177 | ||
178 | void vb2_dma_contig_cleanup_ctx(void *alloc_ctx) | |
179 | { | |
180 | kfree(alloc_ctx); | |
181 | } | |
182 | EXPORT_SYMBOL_GPL(vb2_dma_contig_cleanup_ctx); | |
183 | ||
184 | MODULE_DESCRIPTION("DMA-contig memory handling routines for videobuf2"); | |
95072084 | 185 | MODULE_AUTHOR("Pawel Osciak <pawel@osciak.com>"); |
1a758d4e | 186 | MODULE_LICENSE("GPL"); |