diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-08 17:05:09 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-01-08 17:05:09 -0500 |
commit | 8e369672af8700299ab372bad9397f230b1d591a (patch) | |
tree | 8676cfdb7f8c1ac5ee124aef050d541d70f876d0 /drivers | |
parent | 6950d76c531671ec389e36183311826597951ac6 (diff) | |
parent | a125a3945c950caef001f22055bf201a36568533 (diff) |
Merge branch 'dma-buf-merge' of git://people.freedesktop.org/~airlied/linux
* 'dma-buf-merge' of git://people.freedesktop.org/~airlied/linux:
dma-buf: mark EXPERIMENTAL for 1st release.
dma-buf: Documentation for buffer sharing framework
dma-buf: Introduce dma buffer sharing mechanism
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/Kconfig | 11 | ||||
-rw-r--r-- | drivers/base/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/dma-buf.c | 291 |
3 files changed, 303 insertions, 0 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 21cf46f45245..e95c67edb2cb 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig | |||
@@ -174,4 +174,15 @@ config SYS_HYPERVISOR | |||
174 | 174 | ||
175 | source "drivers/base/regmap/Kconfig" | 175 | source "drivers/base/regmap/Kconfig" |
176 | 176 | ||
177 | config DMA_SHARED_BUFFER | ||
178 | bool "Buffer framework to be shared between drivers" | ||
179 | default n | ||
180 | select ANON_INODES | ||
181 | depends on EXPERIMENTAL | ||
182 | help | ||
183 | This option enables the framework for buffer-sharing between | ||
184 | multiple drivers. A buffer is associated with a file using driver | ||
185 | APIs extension; the file's descriptor can then be passed on to other | ||
186 | driver. | ||
187 | |||
177 | endmenu | 188 | endmenu |
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 1334d893b560..2c8272dd93c4 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile | |||
@@ -9,6 +9,7 @@ obj-$(CONFIG_DEVTMPFS) += devtmpfs.o | |||
9 | obj-y += power/ | 9 | obj-y += power/ |
10 | obj-$(CONFIG_HAS_DMA) += dma-mapping.o | 10 | obj-$(CONFIG_HAS_DMA) += dma-mapping.o |
11 | obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o | 11 | obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o |
12 | obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o | ||
12 | obj-$(CONFIG_ISA) += isa.o | 13 | obj-$(CONFIG_ISA) += isa.o |
13 | obj-$(CONFIG_FW_LOADER) += firmware_class.o | 14 | obj-$(CONFIG_FW_LOADER) += firmware_class.o |
14 | obj-$(CONFIG_NUMA) += node.o | 15 | obj-$(CONFIG_NUMA) += node.o |
diff --git a/drivers/base/dma-buf.c b/drivers/base/dma-buf.c new file mode 100644 index 000000000000..e38ad243b4bb --- /dev/null +++ b/drivers/base/dma-buf.c | |||
@@ -0,0 +1,291 @@ | |||
1 | /* | ||
2 | * Framework for buffer objects that can be shared across devices/subsystems. | ||
3 | * | ||
4 | * Copyright(C) 2011 Linaro Limited. All rights reserved. | ||
5 | * Author: Sumit Semwal <sumit.semwal@ti.com> | ||
6 | * | ||
7 | * Many thanks to linaro-mm-sig list, and specially | ||
8 | * Arnd Bergmann <arnd@arndb.de>, Rob Clark <rob@ti.com> and | ||
9 | * Daniel Vetter <daniel@ffwll.ch> for their support in creation and | ||
10 | * refining of this idea. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify it | ||
13 | * under the terms of the GNU General Public License version 2 as published by | ||
14 | * the Free Software Foundation. | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
17 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
18 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
19 | * more details. | ||
20 | * | ||
21 | * You should have received a copy of the GNU General Public License along with | ||
22 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
23 | */ | ||
24 | |||
25 | #include <linux/fs.h> | ||
26 | #include <linux/slab.h> | ||
27 | #include <linux/dma-buf.h> | ||
28 | #include <linux/anon_inodes.h> | ||
29 | #include <linux/export.h> | ||
30 | |||
31 | static inline int is_dma_buf_file(struct file *); | ||
32 | |||
33 | static int dma_buf_release(struct inode *inode, struct file *file) | ||
34 | { | ||
35 | struct dma_buf *dmabuf; | ||
36 | |||
37 | if (!is_dma_buf_file(file)) | ||
38 | return -EINVAL; | ||
39 | |||
40 | dmabuf = file->private_data; | ||
41 | |||
42 | dmabuf->ops->release(dmabuf); | ||
43 | kfree(dmabuf); | ||
44 | return 0; | ||
45 | } | ||
46 | |||
47 | static const struct file_operations dma_buf_fops = { | ||
48 | .release = dma_buf_release, | ||
49 | }; | ||
50 | |||
51 | /* | ||
52 | * is_dma_buf_file - Check if struct file* is associated with dma_buf | ||
53 | */ | ||
54 | static inline int is_dma_buf_file(struct file *file) | ||
55 | { | ||
56 | return file->f_op == &dma_buf_fops; | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * dma_buf_export - Creates a new dma_buf, and associates an anon file | ||
61 | * with this buffer, so it can be exported. | ||
62 | * Also connect the allocator specific data and ops to the buffer. | ||
63 | * | ||
64 | * @priv: [in] Attach private data of allocator to this buffer | ||
65 | * @ops: [in] Attach allocator-defined dma buf ops to the new buffer. | ||
66 | * @size: [in] Size of the buffer | ||
67 | * @flags: [in] mode flags for the file. | ||
68 | * | ||
69 | * Returns, on success, a newly created dma_buf object, which wraps the | ||
70 | * supplied private data and operations for dma_buf_ops. On either missing | ||
71 | * ops, or error in allocating struct dma_buf, will return negative error. | ||
72 | * | ||
73 | */ | ||
74 | struct dma_buf *dma_buf_export(void *priv, struct dma_buf_ops *ops, | ||
75 | size_t size, int flags) | ||
76 | { | ||
77 | struct dma_buf *dmabuf; | ||
78 | struct file *file; | ||
79 | |||
80 | if (WARN_ON(!priv || !ops | ||
81 | || !ops->map_dma_buf | ||
82 | || !ops->unmap_dma_buf | ||
83 | || !ops->release)) { | ||
84 | return ERR_PTR(-EINVAL); | ||
85 | } | ||
86 | |||
87 | dmabuf = kzalloc(sizeof(struct dma_buf), GFP_KERNEL); | ||
88 | if (dmabuf == NULL) | ||
89 | return ERR_PTR(-ENOMEM); | ||
90 | |||
91 | dmabuf->priv = priv; | ||
92 | dmabuf->ops = ops; | ||
93 | dmabuf->size = size; | ||
94 | |||
95 | file = anon_inode_getfile("dmabuf", &dma_buf_fops, dmabuf, flags); | ||
96 | |||
97 | dmabuf->file = file; | ||
98 | |||
99 | mutex_init(&dmabuf->lock); | ||
100 | INIT_LIST_HEAD(&dmabuf->attachments); | ||
101 | |||
102 | return dmabuf; | ||
103 | } | ||
104 | EXPORT_SYMBOL_GPL(dma_buf_export); | ||
105 | |||
106 | |||
107 | /** | ||
108 | * dma_buf_fd - returns a file descriptor for the given dma_buf | ||
109 | * @dmabuf: [in] pointer to dma_buf for which fd is required. | ||
110 | * | ||
111 | * On success, returns an associated 'fd'. Else, returns error. | ||
112 | */ | ||
113 | int dma_buf_fd(struct dma_buf *dmabuf) | ||
114 | { | ||
115 | int error, fd; | ||
116 | |||
117 | if (!dmabuf || !dmabuf->file) | ||
118 | return -EINVAL; | ||
119 | |||
120 | error = get_unused_fd(); | ||
121 | if (error < 0) | ||
122 | return error; | ||
123 | fd = error; | ||
124 | |||
125 | fd_install(fd, dmabuf->file); | ||
126 | |||
127 | return fd; | ||
128 | } | ||
129 | EXPORT_SYMBOL_GPL(dma_buf_fd); | ||
130 | |||
131 | /** | ||
132 | * dma_buf_get - returns the dma_buf structure related to an fd | ||
133 | * @fd: [in] fd associated with the dma_buf to be returned | ||
134 | * | ||
135 | * On success, returns the dma_buf structure associated with an fd; uses | ||
136 | * file's refcounting done by fget to increase refcount. returns ERR_PTR | ||
137 | * otherwise. | ||
138 | */ | ||
139 | struct dma_buf *dma_buf_get(int fd) | ||
140 | { | ||
141 | struct file *file; | ||
142 | |||
143 | file = fget(fd); | ||
144 | |||
145 | if (!file) | ||
146 | return ERR_PTR(-EBADF); | ||
147 | |||
148 | if (!is_dma_buf_file(file)) { | ||
149 | fput(file); | ||
150 | return ERR_PTR(-EINVAL); | ||
151 | } | ||
152 | |||
153 | return file->private_data; | ||
154 | } | ||
155 | EXPORT_SYMBOL_GPL(dma_buf_get); | ||
156 | |||
157 | /** | ||
158 | * dma_buf_put - decreases refcount of the buffer | ||
159 | * @dmabuf: [in] buffer to reduce refcount of | ||
160 | * | ||
161 | * Uses file's refcounting done implicitly by fput() | ||
162 | */ | ||
163 | void dma_buf_put(struct dma_buf *dmabuf) | ||
164 | { | ||
165 | if (WARN_ON(!dmabuf || !dmabuf->file)) | ||
166 | return; | ||
167 | |||
168 | fput(dmabuf->file); | ||
169 | } | ||
170 | EXPORT_SYMBOL_GPL(dma_buf_put); | ||
171 | |||
172 | /** | ||
173 | * dma_buf_attach - Add the device to dma_buf's attachments list; optionally, | ||
174 | * calls attach() of dma_buf_ops to allow device-specific attach functionality | ||
175 | * @dmabuf: [in] buffer to attach device to. | ||
176 | * @dev: [in] device to be attached. | ||
177 | * | ||
178 | * Returns struct dma_buf_attachment * for this attachment; may return negative | ||
179 | * error codes. | ||
180 | * | ||
181 | */ | ||
182 | struct dma_buf_attachment *dma_buf_attach(struct dma_buf *dmabuf, | ||
183 | struct device *dev) | ||
184 | { | ||
185 | struct dma_buf_attachment *attach; | ||
186 | int ret; | ||
187 | |||
188 | if (WARN_ON(!dmabuf || !dev || !dmabuf->ops)) | ||
189 | return ERR_PTR(-EINVAL); | ||
190 | |||
191 | attach = kzalloc(sizeof(struct dma_buf_attachment), GFP_KERNEL); | ||
192 | if (attach == NULL) | ||
193 | goto err_alloc; | ||
194 | |||
195 | mutex_lock(&dmabuf->lock); | ||
196 | |||
197 | attach->dev = dev; | ||
198 | attach->dmabuf = dmabuf; | ||
199 | if (dmabuf->ops->attach) { | ||
200 | ret = dmabuf->ops->attach(dmabuf, dev, attach); | ||
201 | if (ret) | ||
202 | goto err_attach; | ||
203 | } | ||
204 | list_add(&attach->node, &dmabuf->attachments); | ||
205 | |||
206 | mutex_unlock(&dmabuf->lock); | ||
207 | return attach; | ||
208 | |||
209 | err_alloc: | ||
210 | return ERR_PTR(-ENOMEM); | ||
211 | err_attach: | ||
212 | kfree(attach); | ||
213 | mutex_unlock(&dmabuf->lock); | ||
214 | return ERR_PTR(ret); | ||
215 | } | ||
216 | EXPORT_SYMBOL_GPL(dma_buf_attach); | ||
217 | |||
218 | /** | ||
219 | * dma_buf_detach - Remove the given attachment from dmabuf's attachments list; | ||
220 | * optionally calls detach() of dma_buf_ops for device-specific detach | ||
221 | * @dmabuf: [in] buffer to detach from. | ||
222 | * @attach: [in] attachment to be detached; is free'd after this call. | ||
223 | * | ||
224 | */ | ||
225 | void dma_buf_detach(struct dma_buf *dmabuf, struct dma_buf_attachment *attach) | ||
226 | { | ||
227 | if (WARN_ON(!dmabuf || !attach || !dmabuf->ops)) | ||
228 | return; | ||
229 | |||
230 | mutex_lock(&dmabuf->lock); | ||
231 | list_del(&attach->node); | ||
232 | if (dmabuf->ops->detach) | ||
233 | dmabuf->ops->detach(dmabuf, attach); | ||
234 | |||
235 | mutex_unlock(&dmabuf->lock); | ||
236 | kfree(attach); | ||
237 | } | ||
238 | EXPORT_SYMBOL_GPL(dma_buf_detach); | ||
239 | |||
240 | /** | ||
241 | * dma_buf_map_attachment - Returns the scatterlist table of the attachment; | ||
242 | * mapped into _device_ address space. Is a wrapper for map_dma_buf() of the | ||
243 | * dma_buf_ops. | ||
244 | * @attach: [in] attachment whose scatterlist is to be returned | ||
245 | * @direction: [in] direction of DMA transfer | ||
246 | * | ||
247 | * Returns sg_table containing the scatterlist to be returned; may return NULL | ||
248 | * or ERR_PTR. | ||
249 | * | ||
250 | */ | ||
251 | struct sg_table *dma_buf_map_attachment(struct dma_buf_attachment *attach, | ||
252 | enum dma_data_direction direction) | ||
253 | { | ||
254 | struct sg_table *sg_table = ERR_PTR(-EINVAL); | ||
255 | |||
256 | might_sleep(); | ||
257 | |||
258 | if (WARN_ON(!attach || !attach->dmabuf || !attach->dmabuf->ops)) | ||
259 | return ERR_PTR(-EINVAL); | ||
260 | |||
261 | mutex_lock(&attach->dmabuf->lock); | ||
262 | if (attach->dmabuf->ops->map_dma_buf) | ||
263 | sg_table = attach->dmabuf->ops->map_dma_buf(attach, direction); | ||
264 | mutex_unlock(&attach->dmabuf->lock); | ||
265 | |||
266 | return sg_table; | ||
267 | } | ||
268 | EXPORT_SYMBOL_GPL(dma_buf_map_attachment); | ||
269 | |||
270 | /** | ||
271 | * dma_buf_unmap_attachment - unmaps and decreases usecount of the buffer;might | ||
272 | * deallocate the scatterlist associated. Is a wrapper for unmap_dma_buf() of | ||
273 | * dma_buf_ops. | ||
274 | * @attach: [in] attachment to unmap buffer from | ||
275 | * @sg_table: [in] scatterlist info of the buffer to unmap | ||
276 | * | ||
277 | */ | ||
278 | void dma_buf_unmap_attachment(struct dma_buf_attachment *attach, | ||
279 | struct sg_table *sg_table) | ||
280 | { | ||
281 | if (WARN_ON(!attach || !attach->dmabuf || !sg_table | ||
282 | || !attach->dmabuf->ops)) | ||
283 | return; | ||
284 | |||
285 | mutex_lock(&attach->dmabuf->lock); | ||
286 | if (attach->dmabuf->ops->unmap_dma_buf) | ||
287 | attach->dmabuf->ops->unmap_dma_buf(attach, sg_table); | ||
288 | mutex_unlock(&attach->dmabuf->lock); | ||
289 | |||
290 | } | ||
291 | EXPORT_SYMBOL_GPL(dma_buf_unmap_attachment); | ||