Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0
2 : : /*
3 : : * VideoCore Shared Memory driver using CMA.
4 : : *
5 : : * Copyright: 2018, Raspberry Pi (Trading) Ltd
6 : : * Dave Stevenson <dave.stevenson@raspberrypi.org>
7 : : *
8 : : * Based on vmcs_sm driver from Broadcom Corporation for some API,
9 : : * and taking some code for buffer allocation and dmabuf handling from
10 : : * videobuf2.
11 : : *
12 : : *
13 : : * This driver has 3 main uses:
14 : : * 1) Allocating buffers for the kernel or userspace that can be shared with the
15 : : * VPU.
16 : : * 2) Importing dmabufs from elsewhere for sharing with the VPU.
17 : : * 3) Allocating buffers for use by the VPU.
18 : : *
19 : : * In the first and second cases the native handle is a dmabuf. Releasing the
20 : : * resource inherently comes from releasing the dmabuf, and this will trigger
21 : : * unmapping on the VPU. The underlying allocation and our buffer structure are
22 : : * retained until the VPU has confirmed that it has finished with it.
23 : : *
24 : : * For the VPU allocations the VPU is responsible for triggering the release,
25 : : * and therefore the released message decrements the dma_buf refcount (with the
26 : : * VPU mapping having already been marked as released).
27 : : */
28 : :
29 : : /* ---- Include Files ----------------------------------------------------- */
30 : : #include <linux/cdev.h>
31 : : #include <linux/device.h>
32 : : #include <linux/debugfs.h>
33 : : #include <linux/dma-mapping.h>
34 : : #include <linux/dma-buf.h>
35 : : #include <linux/errno.h>
36 : : #include <linux/fs.h>
37 : : #include <linux/kernel.h>
38 : : #include <linux/list.h>
39 : : #include <linux/miscdevice.h>
40 : : #include <linux/module.h>
41 : : #include <linux/mm.h>
42 : : #include <linux/of_device.h>
43 : : #include <linux/platform_device.h>
44 : : #include <linux/proc_fs.h>
45 : : #include <linux/slab.h>
46 : : #include <linux/seq_file.h>
47 : : #include <linux/syscalls.h>
48 : : #include <linux/types.h>
49 : : #include <asm/cacheflush.h>
50 : :
51 : : #include "vchiq_connected.h"
52 : : #include "vc_sm_cma_vchi.h"
53 : :
54 : : #include "vc_sm.h"
55 : : #include "vc_sm_knl.h"
56 : : #include <linux/broadcom/vc_sm_cma_ioctl.h>
57 : :
58 : : /* ---- Private Constants and Types --------------------------------------- */
59 : :
60 : : #define DEVICE_NAME "vcsm-cma"
61 : : #define DEVICE_MINOR 0
62 : :
63 : : #define VC_SM_RESOURCE_NAME_DEFAULT "sm-host-resource"
64 : :
65 : : #define VC_SM_DIR_ROOT_NAME "vcsm-cma"
66 : : #define VC_SM_STATE "state"
67 : :
68 : : /* Private file data associated with each opened device. */
69 : : struct vc_sm_privdata_t {
70 : : pid_t pid; /* PID of creator. */
71 : :
72 : : int restart_sys; /* Tracks restart on interrupt. */
73 : : enum vc_sm_msg_type int_action; /* Interrupted action. */
74 : : u32 int_trans_id; /* Interrupted transaction. */
75 : : };
76 : :
77 : : typedef int (*VC_SM_SHOW) (struct seq_file *s, void *v);
78 : : struct sm_pde_t {
79 : : VC_SM_SHOW show; /* Debug fs function hookup. */
80 : : struct dentry *dir_entry; /* Debug fs directory entry. */
81 : : void *priv_data; /* Private data */
82 : : };
83 : :
84 : : /* Global state information. */
85 : : struct sm_state_t {
86 : : struct platform_device *pdev;
87 : :
88 : : struct miscdevice misc_dev;
89 : :
90 : : struct sm_instance *sm_handle; /* Handle for videocore service. */
91 : :
92 : : spinlock_t kernelid_map_lock; /* Spinlock protecting kernelid_map */
93 : : struct idr kernelid_map;
94 : :
95 : : struct mutex map_lock; /* Global map lock. */
96 : : struct list_head buffer_list; /* List of buffer. */
97 : :
98 : : struct vc_sm_privdata_t *data_knl; /* Kernel internal data tracking. */
99 : : struct vc_sm_privdata_t *vpu_allocs; /* All allocations from the VPU */
100 : : struct dentry *dir_root; /* Debug fs entries root. */
101 : : struct sm_pde_t dir_state; /* Debug fs entries state sub-tree. */
102 : :
103 : : bool require_released_callback; /* VPU will send a released msg when it
104 : : * has finished with a resource.
105 : : */
106 : : u32 int_trans_id; /* Interrupted transaction. */
107 : : };
108 : :
109 : : struct vc_sm_dma_buf_attachment {
110 : : struct device *dev;
111 : : struct sg_table sg_table;
112 : : struct list_head list;
113 : : enum dma_data_direction dma_dir;
114 : : };
115 : :
116 : : /* ---- Private Variables ----------------------------------------------- */
117 : :
118 : : static struct sm_state_t *sm_state;
119 : : static int sm_inited;
120 : :
121 : : /* ---- Private Function Prototypes -------------------------------------- */
122 : :
123 : : /* ---- Private Functions ------------------------------------------------ */
124 : :
125 : 0 : static int get_kernel_id(struct vc_sm_buffer *buffer)
126 : : {
127 : : int handle;
128 : :
129 : 0 : spin_lock(&sm_state->kernelid_map_lock);
130 : 0 : handle = idr_alloc(&sm_state->kernelid_map, buffer, 0, 0, GFP_KERNEL);
131 : 0 : spin_unlock(&sm_state->kernelid_map_lock);
132 : :
133 : 0 : return handle;
134 : : }
135 : :
136 : : static struct vc_sm_buffer *lookup_kernel_id(int handle)
137 : : {
138 : 0 : return idr_find(&sm_state->kernelid_map, handle);
139 : : }
140 : :
141 : 0 : static void free_kernel_id(int handle)
142 : : {
143 : 0 : spin_lock(&sm_state->kernelid_map_lock);
144 : 0 : idr_remove(&sm_state->kernelid_map, handle);
145 : 0 : spin_unlock(&sm_state->kernelid_map_lock);
146 : 0 : }
147 : :
148 : 0 : static int vc_sm_cma_seq_file_show(struct seq_file *s, void *v)
149 : : {
150 : : struct sm_pde_t *sm_pde;
151 : :
152 : 0 : sm_pde = (struct sm_pde_t *)(s->private);
153 : :
154 : 0 : if (sm_pde && sm_pde->show)
155 : 0 : sm_pde->show(s, v);
156 : :
157 : 0 : return 0;
158 : : }
159 : :
160 : 0 : static int vc_sm_cma_single_open(struct inode *inode, struct file *file)
161 : : {
162 : 0 : return single_open(file, vc_sm_cma_seq_file_show, inode->i_private);
163 : : }
164 : :
165 : : static const struct file_operations vc_sm_cma_debug_fs_fops = {
166 : : .open = vc_sm_cma_single_open,
167 : : .read = seq_read,
168 : : .llseek = seq_lseek,
169 : : .release = single_release,
170 : : };
171 : :
172 : 0 : static int vc_sm_cma_global_state_show(struct seq_file *s, void *v)
173 : : {
174 : : struct vc_sm_buffer *resource = NULL;
175 : : int resource_count = 0;
176 : :
177 : 0 : if (!sm_state)
178 : : return 0;
179 : :
180 : 0 : seq_printf(s, "\nVC-ServiceHandle %p\n", sm_state->sm_handle);
181 : :
182 : : /* Log all applicable mapping(s). */
183 : :
184 : 0 : mutex_lock(&sm_state->map_lock);
185 : 0 : seq_puts(s, "\nResources\n");
186 : 0 : if (!list_empty(&sm_state->buffer_list)) {
187 : 0 : list_for_each_entry(resource, &sm_state->buffer_list,
188 : : global_buffer_list) {
189 : 0 : resource_count++;
190 : :
191 : 0 : seq_printf(s, "\nResource %p\n",
192 : : resource);
193 : 0 : seq_printf(s, " NAME %s\n",
194 : 0 : resource->name);
195 : 0 : seq_printf(s, " SIZE %zu\n",
196 : : resource->size);
197 : 0 : seq_printf(s, " DMABUF %p\n",
198 : : resource->dma_buf);
199 : 0 : if (resource->imported) {
200 : 0 : seq_printf(s, " ATTACH %p\n",
201 : : resource->import.attach);
202 : 0 : seq_printf(s, " SGT %p\n",
203 : : resource->import.sgt);
204 : : } else {
205 : 0 : seq_printf(s, " SGT %p\n",
206 : : resource->alloc.sg_table);
207 : : }
208 : 0 : seq_printf(s, " DMA_ADDR %pad\n",
209 : : &resource->dma_addr);
210 : 0 : seq_printf(s, " VC_HANDLE %08x\n",
211 : : resource->vc_handle);
212 : 0 : seq_printf(s, " VC_MAPPING %d\n",
213 : 0 : resource->vpu_state);
214 : : }
215 : : }
216 : 0 : seq_printf(s, "\n\nTotal resource count: %d\n\n", resource_count);
217 : :
218 : 0 : mutex_unlock(&sm_state->map_lock);
219 : :
220 : 0 : return 0;
221 : : }
222 : :
223 : : /*
224 : : * Adds a buffer to the private data list which tracks all the allocated
225 : : * data.
226 : : */
227 : 0 : static void vc_sm_add_resource(struct vc_sm_privdata_t *privdata,
228 : : struct vc_sm_buffer *buffer)
229 : : {
230 : 0 : mutex_lock(&sm_state->map_lock);
231 : 0 : list_add(&buffer->global_buffer_list, &sm_state->buffer_list);
232 : 0 : mutex_unlock(&sm_state->map_lock);
233 : :
234 : : pr_debug("[%s]: added buffer %p (name %s, size %zu)\n",
235 : : __func__, buffer, buffer->name, buffer->size);
236 : 0 : }
237 : :
238 : : /*
239 : : * Cleans up imported dmabuf.
240 : : */
241 : 0 : static void vc_sm_clean_up_dmabuf(struct vc_sm_buffer *buffer)
242 : : {
243 : 0 : if (!buffer->imported)
244 : 0 : return;
245 : :
246 : : /* Handle cleaning up imported dmabufs */
247 : 0 : mutex_lock(&buffer->lock);
248 : 0 : if (buffer->import.sgt) {
249 : 0 : dma_buf_unmap_attachment(buffer->import.attach,
250 : : buffer->import.sgt,
251 : : DMA_BIDIRECTIONAL);
252 : 0 : buffer->import.sgt = NULL;
253 : : }
254 : 0 : if (buffer->import.attach) {
255 : 0 : dma_buf_detach(buffer->dma_buf, buffer->import.attach);
256 : 0 : buffer->import.attach = NULL;
257 : : }
258 : 0 : mutex_unlock(&buffer->lock);
259 : : }
260 : :
261 : : /*
262 : : * Instructs VPU to decrement the refcount on a buffer.
263 : : */
264 : 0 : static void vc_sm_vpu_free(struct vc_sm_buffer *buffer)
265 : : {
266 : 0 : if (buffer->vc_handle && buffer->vpu_state == VPU_MAPPED) {
267 : 0 : struct vc_sm_free_t free = { buffer->vc_handle, 0 };
268 : 0 : int status = vc_sm_cma_vchi_free(sm_state->sm_handle, &free,
269 : : &sm_state->int_trans_id);
270 : 0 : if (status != 0 && status != -EINTR) {
271 : 0 : pr_err("[%s]: failed to free memory on videocore (status: %u, trans_id: %u)\n",
272 : : __func__, status, sm_state->int_trans_id);
273 : : }
274 : :
275 : 0 : if (sm_state->require_released_callback) {
276 : : /* Need to wait for the VPU to confirm the free. */
277 : :
278 : : /* Retain a reference on this until the VPU has
279 : : * released it
280 : : */
281 : 0 : buffer->vpu_state = VPU_UNMAPPING;
282 : : } else {
283 : 0 : buffer->vpu_state = VPU_NOT_MAPPED;
284 : 0 : buffer->vc_handle = 0;
285 : : }
286 : : }
287 : 0 : }
288 : :
289 : : /*
290 : : * Release an allocation.
291 : : * All refcounting is done via the dma buf object.
292 : : *
293 : : * Must be called with the mutex held. The function will either release the
294 : : * mutex (if defering the release) or destroy it. The caller must therefore not
295 : : * reuse the buffer on return.
296 : : */
297 : 0 : static void vc_sm_release_resource(struct vc_sm_buffer *buffer)
298 : : {
299 : : pr_debug("[%s]: buffer %p (name %s, size %zu), imported %u\n",
300 : : __func__, buffer, buffer->name, buffer->size,
301 : : buffer->imported);
302 : :
303 : 0 : if (buffer->vc_handle) {
304 : : /* We've sent the unmap request but not had the response. */
305 : : pr_debug("[%s]: Waiting for VPU unmap response on %p\n",
306 : : __func__, buffer);
307 : : goto defer;
308 : : }
309 : 0 : if (buffer->in_use) {
310 : : /* dmabuf still in use - we await the release */
311 : : pr_debug("[%s]: buffer %p is still in use\n", __func__, buffer);
312 : : goto defer;
313 : : }
314 : :
315 : : /* Release the allocation (whether imported dmabuf or CMA allocation) */
316 : 0 : if (buffer->imported) {
317 : 0 : if (buffer->import.dma_buf)
318 : 0 : dma_buf_put(buffer->import.dma_buf);
319 : : else
320 : 0 : pr_err("%s: Imported dmabuf already been put for buf %p\n",
321 : : __func__, buffer);
322 : 0 : buffer->import.dma_buf = NULL;
323 : : } else {
324 : 0 : dma_free_coherent(&sm_state->pdev->dev, buffer->size,
325 : : buffer->cookie, buffer->dma_addr);
326 : : }
327 : :
328 : : /* Free our buffer. Start by removing it from the list */
329 : 0 : mutex_lock(&sm_state->map_lock);
330 : : list_del(&buffer->global_buffer_list);
331 : 0 : mutex_unlock(&sm_state->map_lock);
332 : :
333 : : pr_debug("%s: Release our allocation - done\n", __func__);
334 : 0 : mutex_unlock(&buffer->lock);
335 : :
336 : : mutex_destroy(&buffer->lock);
337 : :
338 : 0 : kfree(buffer);
339 : 0 : return;
340 : :
341 : : defer:
342 : 0 : mutex_unlock(&buffer->lock);
343 : : }
344 : :
345 : : /* Create support for private data tracking. */
346 : 3 : static struct vc_sm_privdata_t *vc_sm_cma_create_priv_data(pid_t id)
347 : : {
348 : : char alloc_name[32];
349 : : struct vc_sm_privdata_t *file_data = NULL;
350 : :
351 : : /* Allocate private structure. */
352 : 3 : file_data = kzalloc(sizeof(*file_data), GFP_KERNEL);
353 : :
354 : 3 : if (!file_data)
355 : : return NULL;
356 : :
357 : 3 : snprintf(alloc_name, sizeof(alloc_name), "%d", id);
358 : :
359 : 3 : file_data->pid = id;
360 : :
361 : 3 : return file_data;
362 : : }
363 : :
364 : : /* Dma buf operations for use with our own allocations */
365 : :
366 : 0 : static int vc_sm_dma_buf_attach(struct dma_buf *dmabuf,
367 : : struct dma_buf_attachment *attachment)
368 : :
369 : : {
370 : : struct vc_sm_dma_buf_attachment *a;
371 : : struct sg_table *sgt;
372 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
373 : : struct scatterlist *rd, *wr;
374 : : int ret, i;
375 : :
376 : 0 : a = kzalloc(sizeof(*a), GFP_KERNEL);
377 : 0 : if (!a)
378 : : return -ENOMEM;
379 : :
380 : : pr_debug("%s dmabuf %p attachment %p\n", __func__, dmabuf, attachment);
381 : :
382 : 0 : mutex_lock(&buf->lock);
383 : :
384 : 0 : INIT_LIST_HEAD(&a->list);
385 : :
386 : 0 : sgt = &a->sg_table;
387 : :
388 : : /* Copy the buf->base_sgt scatter list to the attachment, as we can't
389 : : * map the same scatter list to multiple attachments at the same time.
390 : : */
391 : 0 : ret = sg_alloc_table(sgt, buf->alloc.sg_table->orig_nents, GFP_KERNEL);
392 : 0 : if (ret) {
393 : 0 : kfree(a);
394 : 0 : return -ENOMEM;
395 : : }
396 : :
397 : 0 : rd = buf->alloc.sg_table->sgl;
398 : 0 : wr = sgt->sgl;
399 : 0 : for (i = 0; i < sgt->orig_nents; ++i) {
400 : 0 : sg_set_page(wr, sg_page(rd), rd->length, rd->offset);
401 : 0 : rd = sg_next(rd);
402 : 0 : wr = sg_next(wr);
403 : : }
404 : :
405 : 0 : a->dma_dir = DMA_NONE;
406 : 0 : attachment->priv = a;
407 : :
408 : 0 : list_add(&a->list, &buf->attachments);
409 : 0 : mutex_unlock(&buf->lock);
410 : :
411 : 0 : return 0;
412 : : }
413 : :
414 : 0 : static void vc_sm_dma_buf_detach(struct dma_buf *dmabuf,
415 : : struct dma_buf_attachment *attachment)
416 : : {
417 : 0 : struct vc_sm_dma_buf_attachment *a = attachment->priv;
418 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
419 : : struct sg_table *sgt;
420 : :
421 : : pr_debug("%s dmabuf %p attachment %p\n", __func__, dmabuf, attachment);
422 : 0 : if (!a)
423 : 0 : return;
424 : :
425 : 0 : sgt = &a->sg_table;
426 : :
427 : : /* release the scatterlist cache */
428 : 0 : if (a->dma_dir != DMA_NONE)
429 : 0 : dma_unmap_sg(attachment->dev, sgt->sgl, sgt->orig_nents,
430 : : a->dma_dir);
431 : 0 : sg_free_table(sgt);
432 : :
433 : 0 : mutex_lock(&buf->lock);
434 : : list_del(&a->list);
435 : 0 : mutex_unlock(&buf->lock);
436 : :
437 : 0 : kfree(a);
438 : : }
439 : :
440 : 0 : static struct sg_table *vc_sm_map_dma_buf(struct dma_buf_attachment *attachment,
441 : : enum dma_data_direction direction)
442 : : {
443 : 0 : struct vc_sm_dma_buf_attachment *a = attachment->priv;
444 : : /* stealing dmabuf mutex to serialize map/unmap operations */
445 : 0 : struct mutex *lock = &attachment->dmabuf->lock;
446 : : struct sg_table *table;
447 : :
448 : 0 : mutex_lock(lock);
449 : : pr_debug("%s attachment %p\n", __func__, attachment);
450 : 0 : table = &a->sg_table;
451 : :
452 : : /* return previously mapped sg table */
453 : 0 : if (a->dma_dir == direction) {
454 : 0 : mutex_unlock(lock);
455 : 0 : return table;
456 : : }
457 : :
458 : : /* release any previous cache */
459 : 0 : if (a->dma_dir != DMA_NONE) {
460 : 0 : dma_unmap_sg(attachment->dev, table->sgl, table->orig_nents,
461 : : a->dma_dir);
462 : 0 : a->dma_dir = DMA_NONE;
463 : : }
464 : :
465 : : /* mapping to the client with new direction */
466 : 0 : table->nents = dma_map_sg(attachment->dev, table->sgl,
467 : : table->orig_nents, direction);
468 : 0 : if (!table->nents) {
469 : 0 : pr_err("failed to map scatterlist\n");
470 : 0 : mutex_unlock(lock);
471 : 0 : return ERR_PTR(-EIO);
472 : : }
473 : :
474 : 0 : a->dma_dir = direction;
475 : 0 : mutex_unlock(lock);
476 : :
477 : : pr_debug("%s attachment %p\n", __func__, attachment);
478 : 0 : return table;
479 : : }
480 : :
481 : 0 : static void vc_sm_unmap_dma_buf(struct dma_buf_attachment *attachment,
482 : : struct sg_table *table,
483 : : enum dma_data_direction direction)
484 : : {
485 : : pr_debug("%s attachment %p\n", __func__, attachment);
486 : 0 : dma_unmap_sg(attachment->dev, table->sgl, table->nents, direction);
487 : 0 : }
488 : :
489 : 0 : static int vc_sm_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
490 : : {
491 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
492 : : int ret;
493 : :
494 : : pr_debug("%s dmabuf %p, buf %p, vm_start %08lX\n", __func__, dmabuf,
495 : : buf, vma->vm_start);
496 : :
497 : 0 : mutex_lock(&buf->lock);
498 : :
499 : : /* now map it to userspace */
500 : 0 : vma->vm_pgoff = 0;
501 : :
502 : 0 : ret = dma_mmap_coherent(&sm_state->pdev->dev, vma, buf->cookie,
503 : : buf->dma_addr, buf->size);
504 : :
505 : 0 : if (ret) {
506 : 0 : pr_err("Remapping memory failed, error: %d\n", ret);
507 : 0 : return ret;
508 : : }
509 : :
510 : 0 : vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
511 : :
512 : 0 : mutex_unlock(&buf->lock);
513 : :
514 : 0 : if (ret)
515 : 0 : pr_err("%s: failure mapping buffer to userspace\n",
516 : : __func__);
517 : :
518 : : return ret;
519 : : }
520 : :
521 : 0 : static void vc_sm_dma_buf_release(struct dma_buf *dmabuf)
522 : : {
523 : : struct vc_sm_buffer *buffer;
524 : :
525 : 0 : if (!dmabuf)
526 : 0 : return;
527 : :
528 : 0 : buffer = (struct vc_sm_buffer *)dmabuf->priv;
529 : :
530 : 0 : mutex_lock(&buffer->lock);
531 : :
532 : : pr_debug("%s dmabuf %p, buffer %p\n", __func__, dmabuf, buffer);
533 : :
534 : 0 : buffer->in_use = 0;
535 : :
536 : : /* Unmap on the VPU */
537 : 0 : vc_sm_vpu_free(buffer);
538 : : pr_debug("%s vpu_free done\n", __func__);
539 : :
540 : : /* Unmap our dma_buf object (the vc_sm_buffer remains until released
541 : : * on the VPU).
542 : : */
543 : 0 : vc_sm_clean_up_dmabuf(buffer);
544 : : pr_debug("%s clean_up dmabuf done\n", __func__);
545 : :
546 : : /* buffer->lock will be destroyed by vc_sm_release_resource if finished
547 : : * with, otherwise unlocked. Do NOT unlock here.
548 : : */
549 : 0 : vc_sm_release_resource(buffer);
550 : : pr_debug("%s done\n", __func__);
551 : : }
552 : :
553 : 0 : static int vc_sm_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
554 : : enum dma_data_direction direction)
555 : : {
556 : : struct vc_sm_buffer *buf;
557 : : struct vc_sm_dma_buf_attachment *a;
558 : :
559 : 0 : if (!dmabuf)
560 : : return -EFAULT;
561 : :
562 : 0 : buf = dmabuf->priv;
563 : 0 : if (!buf)
564 : : return -EFAULT;
565 : :
566 : 0 : mutex_lock(&buf->lock);
567 : :
568 : 0 : list_for_each_entry(a, &buf->attachments, list) {
569 : 0 : dma_sync_sg_for_cpu(a->dev, a->sg_table.sgl,
570 : 0 : a->sg_table.nents, direction);
571 : : }
572 : 0 : mutex_unlock(&buf->lock);
573 : :
574 : 0 : return 0;
575 : : }
576 : :
577 : 0 : static int vc_sm_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
578 : : enum dma_data_direction direction)
579 : : {
580 : : struct vc_sm_buffer *buf;
581 : : struct vc_sm_dma_buf_attachment *a;
582 : :
583 : 0 : if (!dmabuf)
584 : : return -EFAULT;
585 : 0 : buf = dmabuf->priv;
586 : 0 : if (!buf)
587 : : return -EFAULT;
588 : :
589 : 0 : mutex_lock(&buf->lock);
590 : :
591 : 0 : list_for_each_entry(a, &buf->attachments, list) {
592 : 0 : dma_sync_sg_for_device(a->dev, a->sg_table.sgl,
593 : 0 : a->sg_table.nents, direction);
594 : : }
595 : 0 : mutex_unlock(&buf->lock);
596 : :
597 : 0 : return 0;
598 : : }
599 : :
600 : 0 : static void *vc_sm_dma_buf_kmap(struct dma_buf *dmabuf, unsigned long offset)
601 : : {
602 : : /* FIXME */
603 : 0 : return NULL;
604 : : }
605 : :
606 : 0 : static void vc_sm_dma_buf_kunmap(struct dma_buf *dmabuf, unsigned long offset,
607 : : void *ptr)
608 : : {
609 : : /* FIXME */
610 : 0 : }
611 : :
612 : : static const struct dma_buf_ops dma_buf_ops = {
613 : : .map_dma_buf = vc_sm_map_dma_buf,
614 : : .unmap_dma_buf = vc_sm_unmap_dma_buf,
615 : : .mmap = vc_sm_dmabuf_mmap,
616 : : .release = vc_sm_dma_buf_release,
617 : : .attach = vc_sm_dma_buf_attach,
618 : : .detach = vc_sm_dma_buf_detach,
619 : : .begin_cpu_access = vc_sm_dma_buf_begin_cpu_access,
620 : : .end_cpu_access = vc_sm_dma_buf_end_cpu_access,
621 : : .map = vc_sm_dma_buf_kmap,
622 : : .unmap = vc_sm_dma_buf_kunmap,
623 : : };
624 : :
625 : : /* Dma_buf operations for chaining through to an imported dma_buf */
626 : :
627 : : static
628 : 0 : int vc_sm_import_dma_buf_attach(struct dma_buf *dmabuf,
629 : : struct dma_buf_attachment *attachment)
630 : : {
631 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
632 : :
633 : 0 : if (!buf->imported)
634 : : return -EINVAL;
635 : 0 : return buf->import.dma_buf->ops->attach(buf->import.dma_buf,
636 : : attachment);
637 : : }
638 : :
639 : : static
640 : 0 : void vc_sm_import_dma_buf_detatch(struct dma_buf *dmabuf,
641 : : struct dma_buf_attachment *attachment)
642 : : {
643 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
644 : :
645 : 0 : if (!buf->imported)
646 : 0 : return;
647 : 0 : buf->import.dma_buf->ops->detach(buf->import.dma_buf, attachment);
648 : : }
649 : :
650 : : static
651 : 0 : struct sg_table *vc_sm_import_map_dma_buf(struct dma_buf_attachment *attachment,
652 : : enum dma_data_direction direction)
653 : : {
654 : 0 : struct vc_sm_buffer *buf = attachment->dmabuf->priv;
655 : :
656 : 0 : if (!buf->imported)
657 : : return NULL;
658 : 0 : return buf->import.dma_buf->ops->map_dma_buf(attachment,
659 : : direction);
660 : : }
661 : :
662 : : static
663 : 0 : void vc_sm_import_unmap_dma_buf(struct dma_buf_attachment *attachment,
664 : : struct sg_table *table,
665 : : enum dma_data_direction direction)
666 : : {
667 : 0 : struct vc_sm_buffer *buf = attachment->dmabuf->priv;
668 : :
669 : 0 : if (!buf->imported)
670 : 0 : return;
671 : 0 : buf->import.dma_buf->ops->unmap_dma_buf(attachment, table, direction);
672 : : }
673 : :
674 : : static
675 : 0 : int vc_sm_import_dmabuf_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
676 : : {
677 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
678 : :
679 : : pr_debug("%s: mmap dma_buf %p, buf %p, imported db %p\n", __func__,
680 : : dmabuf, buf, buf->import.dma_buf);
681 : 0 : if (!buf->imported) {
682 : 0 : pr_err("%s: mmap dma_buf %p- not an imported buffer\n",
683 : : __func__, dmabuf);
684 : 0 : return -EINVAL;
685 : : }
686 : 0 : return buf->import.dma_buf->ops->mmap(buf->import.dma_buf, vma);
687 : : }
688 : :
689 : : static
690 : 0 : void vc_sm_import_dma_buf_release(struct dma_buf *dmabuf)
691 : : {
692 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
693 : :
694 : : pr_debug("%s: Relasing dma_buf %p\n", __func__, dmabuf);
695 : 0 : mutex_lock(&buf->lock);
696 : 0 : if (!buf->imported)
697 : 0 : return;
698 : :
699 : 0 : buf->in_use = 0;
700 : :
701 : 0 : vc_sm_vpu_free(buf);
702 : :
703 : 0 : vc_sm_release_resource(buf);
704 : : }
705 : :
706 : : static
707 : 0 : void *vc_sm_import_dma_buf_kmap(struct dma_buf *dmabuf,
708 : : unsigned long offset)
709 : : {
710 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
711 : :
712 : 0 : if (!buf->imported)
713 : : return NULL;
714 : 0 : return buf->import.dma_buf->ops->map(buf->import.dma_buf, offset);
715 : : }
716 : :
717 : : static
718 : 0 : void vc_sm_import_dma_buf_kunmap(struct dma_buf *dmabuf,
719 : : unsigned long offset, void *ptr)
720 : : {
721 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
722 : :
723 : 0 : if (!buf->imported)
724 : 0 : return;
725 : 0 : buf->import.dma_buf->ops->unmap(buf->import.dma_buf, offset, ptr);
726 : : }
727 : :
728 : : static
729 : 0 : int vc_sm_import_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
730 : : enum dma_data_direction direction)
731 : : {
732 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
733 : :
734 : 0 : if (!buf->imported)
735 : : return -EINVAL;
736 : 0 : return buf->import.dma_buf->ops->begin_cpu_access(buf->import.dma_buf,
737 : : direction);
738 : : }
739 : :
740 : : static
741 : 0 : int vc_sm_import_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
742 : : enum dma_data_direction direction)
743 : : {
744 : 0 : struct vc_sm_buffer *buf = dmabuf->priv;
745 : :
746 : 0 : if (!buf->imported)
747 : : return -EINVAL;
748 : 0 : return buf->import.dma_buf->ops->end_cpu_access(buf->import.dma_buf,
749 : : direction);
750 : : }
751 : :
752 : : static const struct dma_buf_ops dma_buf_import_ops = {
753 : : .map_dma_buf = vc_sm_import_map_dma_buf,
754 : : .unmap_dma_buf = vc_sm_import_unmap_dma_buf,
755 : : .mmap = vc_sm_import_dmabuf_mmap,
756 : : .release = vc_sm_import_dma_buf_release,
757 : : .attach = vc_sm_import_dma_buf_attach,
758 : : .detach = vc_sm_import_dma_buf_detatch,
759 : : .begin_cpu_access = vc_sm_import_dma_buf_begin_cpu_access,
760 : : .end_cpu_access = vc_sm_import_dma_buf_end_cpu_access,
761 : : .map = vc_sm_import_dma_buf_kmap,
762 : : .unmap = vc_sm_import_dma_buf_kunmap,
763 : : };
764 : :
765 : : /* Import a dma_buf to be shared with VC. */
766 : : int
767 : 0 : vc_sm_cma_import_dmabuf_internal(struct vc_sm_privdata_t *private,
768 : : struct dma_buf *dma_buf,
769 : : int fd,
770 : : struct dma_buf **imported_buf)
771 : : {
772 : 0 : DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
773 : : struct vc_sm_buffer *buffer = NULL;
774 : 0 : struct vc_sm_import import = { };
775 : 0 : struct vc_sm_import_result result = { };
776 : : struct dma_buf_attachment *attach = NULL;
777 : : struct sg_table *sgt = NULL;
778 : : dma_addr_t dma_addr;
779 : : int ret = 0;
780 : : int status;
781 : :
782 : : /* Setup our allocation parameters */
783 : : pr_debug("%s: importing dma_buf %p/fd %d\n", __func__, dma_buf, fd);
784 : :
785 : 0 : if (fd < 0)
786 : 0 : get_dma_buf(dma_buf);
787 : : else
788 : 0 : dma_buf = dma_buf_get(fd);
789 : :
790 : 0 : if (!dma_buf)
791 : : return -EINVAL;
792 : :
793 : 0 : attach = dma_buf_attach(dma_buf, &sm_state->pdev->dev);
794 : 0 : if (IS_ERR(attach)) {
795 : : ret = PTR_ERR(attach);
796 : 0 : goto error;
797 : : }
798 : :
799 : 0 : sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
800 : 0 : if (IS_ERR(sgt)) {
801 : : ret = PTR_ERR(sgt);
802 : 0 : goto error;
803 : : }
804 : :
805 : : /* Verify that the address block is contiguous */
806 : 0 : if (sgt->nents != 1) {
807 : : ret = -ENOMEM;
808 : : goto error;
809 : : }
810 : :
811 : : /* Allocate local buffer to track this allocation. */
812 : 0 : buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
813 : 0 : if (!buffer) {
814 : : ret = -ENOMEM;
815 : : goto error;
816 : : }
817 : :
818 : 0 : import.type = VC_SM_ALLOC_NON_CACHED;
819 : 0 : dma_addr = sg_dma_address(sgt->sgl);
820 : 0 : import.addr = (u32)dma_addr;
821 : 0 : if ((import.addr & 0xC0000000) != 0xC0000000) {
822 : 0 : pr_err("%s: Expecting an uncached alias for dma_addr %pad\n",
823 : : __func__, &dma_addr);
824 : 0 : import.addr |= 0xC0000000;
825 : : }
826 : 0 : import.size = sg_dma_len(sgt->sgl);
827 : 0 : import.allocator = current->tgid;
828 : 0 : import.kernel_id = get_kernel_id(buffer);
829 : :
830 : 0 : memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT,
831 : : sizeof(VC_SM_RESOURCE_NAME_DEFAULT));
832 : :
833 : : pr_debug("[%s]: attempt to import \"%s\" data - type %u, addr %pad, size %u.\n",
834 : : __func__, import.name, import.type, &dma_addr, import.size);
835 : :
836 : : /* Allocate the videocore buffer. */
837 : 0 : status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result,
838 : : &sm_state->int_trans_id);
839 : 0 : if (status == -EINTR) {
840 : : pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n",
841 : : __func__, sm_state->int_trans_id);
842 : : ret = -ERESTARTSYS;
843 : 0 : private->restart_sys = -EINTR;
844 : 0 : private->int_action = VC_SM_MSG_TYPE_IMPORT;
845 : 0 : goto error;
846 : 0 : } else if (status || !result.res_handle) {
847 : : pr_debug("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n",
848 : : __func__, status, sm_state->int_trans_id);
849 : : ret = -ENOMEM;
850 : : goto error;
851 : : }
852 : :
853 : 0 : mutex_init(&buffer->lock);
854 : 0 : INIT_LIST_HEAD(&buffer->attachments);
855 : 0 : memcpy(buffer->name, import.name,
856 : : min(sizeof(buffer->name), sizeof(import.name) - 1));
857 : :
858 : : /* Keep track of the buffer we created. */
859 : 0 : buffer->private = private;
860 : 0 : buffer->vc_handle = result.res_handle;
861 : 0 : buffer->size = import.size;
862 : 0 : buffer->vpu_state = VPU_MAPPED;
863 : :
864 : 0 : buffer->imported = 1;
865 : 0 : buffer->import.dma_buf = dma_buf;
866 : :
867 : 0 : buffer->import.attach = attach;
868 : 0 : buffer->import.sgt = sgt;
869 : 0 : buffer->dma_addr = dma_addr;
870 : 0 : buffer->in_use = 1;
871 : 0 : buffer->kernel_id = import.kernel_id;
872 : :
873 : : /*
874 : : * We're done - we need to export a new dmabuf chaining through most
875 : : * functions, but enabling us to release our own internal references
876 : : * here.
877 : : */
878 : 0 : exp_info.ops = &dma_buf_import_ops;
879 : 0 : exp_info.size = import.size;
880 : 0 : exp_info.flags = O_RDWR;
881 : 0 : exp_info.priv = buffer;
882 : :
883 : 0 : buffer->dma_buf = dma_buf_export(&exp_info);
884 : 0 : if (IS_ERR(buffer->dma_buf)) {
885 : : ret = PTR_ERR(buffer->dma_buf);
886 : 0 : goto error;
887 : : }
888 : :
889 : 0 : vc_sm_add_resource(private, buffer);
890 : :
891 : 0 : *imported_buf = buffer->dma_buf;
892 : :
893 : 0 : return 0;
894 : :
895 : : error:
896 : 0 : if (result.res_handle) {
897 : 0 : struct vc_sm_free_t free = { result.res_handle, 0 };
898 : :
899 : 0 : vc_sm_cma_vchi_free(sm_state->sm_handle, &free,
900 : : &sm_state->int_trans_id);
901 : : }
902 : 0 : free_kernel_id(import.kernel_id);
903 : 0 : kfree(buffer);
904 : 0 : if (sgt)
905 : 0 : dma_buf_unmap_attachment(attach, sgt, DMA_BIDIRECTIONAL);
906 : 0 : if (attach)
907 : 0 : dma_buf_detach(dma_buf, attach);
908 : 0 : dma_buf_put(dma_buf);
909 : 0 : return ret;
910 : : }
911 : :
912 : 0 : static int vc_sm_cma_vpu_alloc(u32 size, u32 align, const char *name,
913 : : u32 mem_handle, struct vc_sm_buffer **ret_buffer)
914 : : {
915 : 0 : DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
916 : : struct vc_sm_buffer *buffer = NULL;
917 : : struct sg_table *sgt;
918 : : int aligned_size;
919 : : int ret = 0;
920 : :
921 : : /* Align to the user requested align */
922 : 0 : aligned_size = ALIGN(size, align);
923 : : /* and then to a page boundary */
924 : 0 : aligned_size = PAGE_ALIGN(aligned_size);
925 : :
926 : 0 : if (!aligned_size)
927 : : return -EINVAL;
928 : :
929 : : /* Allocate local buffer to track this allocation. */
930 : 0 : buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
931 : 0 : if (!buffer)
932 : : return -ENOMEM;
933 : :
934 : 0 : mutex_init(&buffer->lock);
935 : : /* Acquire the mutex as vc_sm_release_resource will release it in the
936 : : * error path.
937 : : */
938 : 0 : mutex_lock(&buffer->lock);
939 : :
940 : 0 : buffer->cookie = dma_alloc_coherent(&sm_state->pdev->dev,
941 : : aligned_size, &buffer->dma_addr,
942 : : GFP_KERNEL);
943 : 0 : if (!buffer->cookie) {
944 : 0 : pr_err("[%s]: dma_alloc_coherent alloc of %d bytes failed\n",
945 : : __func__, aligned_size);
946 : : ret = -ENOMEM;
947 : 0 : goto error;
948 : : }
949 : :
950 : : pr_debug("[%s]: alloc of %d bytes success\n",
951 : : __func__, aligned_size);
952 : :
953 : : sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
954 : 0 : if (!sgt) {
955 : : ret = -ENOMEM;
956 : : goto error;
957 : : }
958 : :
959 : 0 : ret = dma_get_sgtable(&sm_state->pdev->dev, sgt, buffer->cookie,
960 : : buffer->dma_addr, buffer->size);
961 : 0 : if (ret < 0) {
962 : 0 : pr_err("failed to get scatterlist from DMA API\n");
963 : 0 : kfree(sgt);
964 : : ret = -ENOMEM;
965 : 0 : goto error;
966 : : }
967 : 0 : buffer->alloc.sg_table = sgt;
968 : :
969 : 0 : INIT_LIST_HEAD(&buffer->attachments);
970 : :
971 : 0 : memcpy(buffer->name, name,
972 : 0 : min(sizeof(buffer->name), strlen(name)));
973 : :
974 : 0 : exp_info.ops = &dma_buf_ops;
975 : 0 : exp_info.size = aligned_size;
976 : 0 : exp_info.flags = O_RDWR;
977 : 0 : exp_info.priv = buffer;
978 : :
979 : 0 : buffer->dma_buf = dma_buf_export(&exp_info);
980 : 0 : if (IS_ERR(buffer->dma_buf)) {
981 : : ret = PTR_ERR(buffer->dma_buf);
982 : 0 : goto error;
983 : : }
984 : 0 : buffer->dma_addr = (u32)sg_dma_address(buffer->alloc.sg_table->sgl);
985 : 0 : if ((buffer->dma_addr & 0xC0000000) != 0xC0000000) {
986 : 0 : pr_warn_once("%s: Expecting an uncached alias for dma_addr %pad\n",
987 : : __func__, &buffer->dma_addr);
988 : 0 : buffer->dma_addr |= 0xC0000000;
989 : : }
990 : 0 : buffer->private = sm_state->vpu_allocs;
991 : :
992 : 0 : buffer->vc_handle = mem_handle;
993 : 0 : buffer->vpu_state = VPU_MAPPED;
994 : 0 : buffer->vpu_allocated = 1;
995 : 0 : buffer->size = size;
996 : : /*
997 : : * Create an ID that will be passed along with our message so
998 : : * that when we service the release reply, we can look up which
999 : : * resource is being released.
1000 : : */
1001 : 0 : buffer->kernel_id = get_kernel_id(buffer);
1002 : :
1003 : 0 : vc_sm_add_resource(sm_state->vpu_allocs, buffer);
1004 : :
1005 : 0 : mutex_unlock(&buffer->lock);
1006 : :
1007 : 0 : *ret_buffer = buffer;
1008 : 0 : return 0;
1009 : : error:
1010 : 0 : if (buffer)
1011 : 0 : vc_sm_release_resource(buffer);
1012 : 0 : return ret;
1013 : : }
1014 : :
1015 : : static void
1016 : 3 : vc_sm_vpu_event(struct sm_instance *instance, struct vc_sm_result_t *reply,
1017 : : int reply_len)
1018 : : {
1019 : 3 : switch (reply->trans_id & ~0x80000000) {
1020 : : case VC_SM_MSG_TYPE_CLIENT_VERSION:
1021 : : {
1022 : : /* Acknowledge that the firmware supports the version command */
1023 : : pr_debug("%s: firmware acked version msg. Require release cb\n",
1024 : : __func__);
1025 : 3 : sm_state->require_released_callback = true;
1026 : : }
1027 : 3 : break;
1028 : : case VC_SM_MSG_TYPE_RELEASED:
1029 : : {
1030 : : struct vc_sm_released *release = (struct vc_sm_released *)reply;
1031 : : struct vc_sm_buffer *buffer =
1032 : 0 : lookup_kernel_id(release->kernel_id);
1033 : 0 : if (!buffer) {
1034 : 0 : pr_err("%s: VC released a buffer that is already released, kernel_id %d\n",
1035 : : __func__, release->kernel_id);
1036 : 0 : break;
1037 : : }
1038 : 0 : mutex_lock(&buffer->lock);
1039 : :
1040 : : pr_debug("%s: Released addr %08x, size %u, id %08x, mem_handle %08x\n",
1041 : : __func__, release->addr, release->size,
1042 : : release->kernel_id, release->vc_handle);
1043 : :
1044 : 0 : buffer->vc_handle = 0;
1045 : 0 : buffer->vpu_state = VPU_NOT_MAPPED;
1046 : 0 : free_kernel_id(release->kernel_id);
1047 : :
1048 : 0 : if (buffer->vpu_allocated) {
1049 : : /* VPU allocation, so release the dmabuf which will
1050 : : * trigger the clean up.
1051 : : */
1052 : 0 : mutex_unlock(&buffer->lock);
1053 : 0 : dma_buf_put(buffer->dma_buf);
1054 : : } else {
1055 : 0 : vc_sm_release_resource(buffer);
1056 : : }
1057 : : }
1058 : : break;
1059 : : case VC_SM_MSG_TYPE_VC_MEM_REQUEST:
1060 : : {
1061 : 0 : struct vc_sm_buffer *buffer = NULL;
1062 : : struct vc_sm_vc_mem_request *req =
1063 : : (struct vc_sm_vc_mem_request *)reply;
1064 : : struct vc_sm_vc_mem_request_result reply;
1065 : : int ret;
1066 : :
1067 : : pr_debug("%s: Request %u bytes of memory, align %d name %s, trans_id %08x\n",
1068 : : __func__, req->size, req->align, req->name,
1069 : : req->trans_id);
1070 : 0 : ret = vc_sm_cma_vpu_alloc(req->size, req->align, req->name,
1071 : : req->vc_handle, &buffer);
1072 : :
1073 : 0 : reply.trans_id = req->trans_id;
1074 : 0 : if (!ret) {
1075 : 0 : reply.addr = buffer->dma_addr;
1076 : 0 : reply.kernel_id = buffer->kernel_id;
1077 : : pr_debug("%s: Allocated resource buffer %p, addr %pad\n",
1078 : : __func__, buffer, &buffer->dma_addr);
1079 : : } else {
1080 : 0 : pr_err("%s: Allocation failed size %u, name %s, vc_handle %u\n",
1081 : : __func__, req->size, req->name, req->vc_handle);
1082 : 0 : reply.addr = 0;
1083 : 0 : reply.kernel_id = 0;
1084 : : }
1085 : 0 : vc_sm_vchi_client_vc_mem_req_reply(sm_state->sm_handle, &reply,
1086 : 0 : &sm_state->int_trans_id);
1087 : : break;
1088 : : }
1089 : : break;
1090 : : default:
1091 : 0 : pr_err("%s: Unknown vpu cmd %x\n", __func__, reply->trans_id);
1092 : 0 : break;
1093 : : }
1094 : 3 : }
1095 : :
1096 : : /* Userspace handling */
1097 : : /*
1098 : : * Open the device. Creates a private state to help track all allocation
1099 : : * associated with this device.
1100 : : */
1101 : 0 : static int vc_sm_cma_open(struct inode *inode, struct file *file)
1102 : : {
1103 : : /* Make sure the device was started properly. */
1104 : 0 : if (!sm_state) {
1105 : 0 : pr_err("[%s]: invalid device\n", __func__);
1106 : 0 : return -EPERM;
1107 : : }
1108 : :
1109 : 0 : file->private_data = vc_sm_cma_create_priv_data(current->tgid);
1110 : 0 : if (!file->private_data) {
1111 : 0 : pr_err("[%s]: failed to create data tracker\n", __func__);
1112 : :
1113 : 0 : return -ENOMEM;
1114 : : }
1115 : :
1116 : : return 0;
1117 : : }
1118 : :
1119 : : /*
1120 : : * Close the vcsm-cma device.
1121 : : * All allocations are file descriptors to the dmabuf objects, so we will get
1122 : : * the clean up request on those as those are cleaned up.
1123 : : */
1124 : 0 : static int vc_sm_cma_release(struct inode *inode, struct file *file)
1125 : : {
1126 : 0 : struct vc_sm_privdata_t *file_data =
1127 : : (struct vc_sm_privdata_t *)file->private_data;
1128 : : int ret = 0;
1129 : :
1130 : : /* Make sure the device was started properly. */
1131 : 0 : if (!sm_state || !file_data) {
1132 : 0 : pr_err("[%s]: invalid device\n", __func__);
1133 : : ret = -EPERM;
1134 : 0 : goto out;
1135 : : }
1136 : :
1137 : : pr_debug("[%s]: using private data %p\n", __func__, file_data);
1138 : :
1139 : : /* Terminate the private data. */
1140 : 0 : kfree(file_data);
1141 : :
1142 : : out:
1143 : 0 : return ret;
1144 : : }
1145 : :
1146 : : /*
1147 : : * Allocate a shared memory handle and block.
1148 : : * Allocation is from CMA, and then imported into the VPU mappings.
1149 : : */
1150 : 0 : int vc_sm_cma_ioctl_alloc(struct vc_sm_privdata_t *private,
1151 : : struct vc_sm_cma_ioctl_alloc *ioparam)
1152 : : {
1153 : 0 : DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
1154 : : struct vc_sm_buffer *buffer = NULL;
1155 : 0 : struct vc_sm_import import = { 0 };
1156 : 0 : struct vc_sm_import_result result = { 0 };
1157 : : struct dma_buf *dmabuf = NULL;
1158 : : struct sg_table *sgt;
1159 : : int aligned_size;
1160 : : int ret = 0;
1161 : : int status;
1162 : : int fd = -1;
1163 : :
1164 : 0 : aligned_size = PAGE_ALIGN(ioparam->size);
1165 : :
1166 : 0 : if (!aligned_size)
1167 : : return -EINVAL;
1168 : :
1169 : : /* Allocate local buffer to track this allocation. */
1170 : 0 : buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
1171 : 0 : if (!buffer) {
1172 : : ret = -ENOMEM;
1173 : : goto error;
1174 : : }
1175 : :
1176 : 0 : buffer->cookie = dma_alloc_coherent(&sm_state->pdev->dev,
1177 : : aligned_size,
1178 : : &buffer->dma_addr,
1179 : : GFP_KERNEL);
1180 : 0 : if (!buffer->cookie) {
1181 : 0 : pr_err("[%s]: dma_alloc_coherent alloc of %d bytes failed\n",
1182 : : __func__, aligned_size);
1183 : : ret = -ENOMEM;
1184 : 0 : goto error;
1185 : : }
1186 : :
1187 : 0 : import.type = VC_SM_ALLOC_NON_CACHED;
1188 : 0 : import.allocator = current->tgid;
1189 : :
1190 : 0 : if (*ioparam->name)
1191 : 0 : memcpy(import.name, ioparam->name, sizeof(import.name) - 1);
1192 : : else
1193 : 0 : memcpy(import.name, VC_SM_RESOURCE_NAME_DEFAULT,
1194 : : sizeof(VC_SM_RESOURCE_NAME_DEFAULT));
1195 : :
1196 : 0 : mutex_init(&buffer->lock);
1197 : 0 : INIT_LIST_HEAD(&buffer->attachments);
1198 : 0 : memcpy(buffer->name, import.name,
1199 : : min(sizeof(buffer->name), sizeof(import.name) - 1));
1200 : :
1201 : 0 : exp_info.ops = &dma_buf_ops;
1202 : 0 : exp_info.size = aligned_size;
1203 : 0 : exp_info.flags = O_RDWR;
1204 : 0 : exp_info.priv = buffer;
1205 : :
1206 : 0 : dmabuf = dma_buf_export(&exp_info);
1207 : 0 : if (IS_ERR(dmabuf)) {
1208 : : ret = PTR_ERR(dmabuf);
1209 : 0 : goto error;
1210 : : }
1211 : 0 : buffer->dma_buf = dmabuf;
1212 : :
1213 : 0 : import.addr = buffer->dma_addr;
1214 : 0 : import.size = aligned_size;
1215 : 0 : import.kernel_id = get_kernel_id(buffer);
1216 : :
1217 : : /* Wrap it into a videocore buffer. */
1218 : 0 : status = vc_sm_cma_vchi_import(sm_state->sm_handle, &import, &result,
1219 : : &sm_state->int_trans_id);
1220 : 0 : if (status == -EINTR) {
1221 : : pr_debug("[%s]: requesting import memory action restart (trans_id: %u)\n",
1222 : : __func__, sm_state->int_trans_id);
1223 : : ret = -ERESTARTSYS;
1224 : 0 : private->restart_sys = -EINTR;
1225 : 0 : private->int_action = VC_SM_MSG_TYPE_IMPORT;
1226 : 0 : goto error;
1227 : 0 : } else if (status || !result.res_handle) {
1228 : 0 : pr_err("[%s]: failed to import memory on videocore (status: %u, trans_id: %u)\n",
1229 : : __func__, status, sm_state->int_trans_id);
1230 : : ret = -ENOMEM;
1231 : 0 : goto error;
1232 : : }
1233 : :
1234 : : /* Keep track of the buffer we created. */
1235 : 0 : buffer->private = private;
1236 : 0 : buffer->vc_handle = result.res_handle;
1237 : 0 : buffer->size = import.size;
1238 : 0 : buffer->vpu_state = VPU_MAPPED;
1239 : 0 : buffer->kernel_id = import.kernel_id;
1240 : :
1241 : : sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
1242 : 0 : if (!sgt) {
1243 : : ret = -ENOMEM;
1244 : : goto error;
1245 : : }
1246 : :
1247 : 0 : ret = dma_get_sgtable(&sm_state->pdev->dev, sgt, buffer->cookie,
1248 : : buffer->dma_addr, buffer->size);
1249 : 0 : if (ret < 0) {
1250 : : /* FIXME: error handling */
1251 : 0 : pr_err("failed to get scatterlist from DMA API\n");
1252 : 0 : kfree(sgt);
1253 : : ret = -ENOMEM;
1254 : 0 : goto error;
1255 : : }
1256 : 0 : buffer->alloc.sg_table = sgt;
1257 : :
1258 : 0 : fd = dma_buf_fd(dmabuf, O_CLOEXEC);
1259 : 0 : if (fd < 0)
1260 : : goto error;
1261 : :
1262 : 0 : vc_sm_add_resource(private, buffer);
1263 : :
1264 : : pr_debug("[%s]: Added resource as fd %d, buffer %p, private %p, dma_addr %pad\n",
1265 : : __func__, fd, buffer, private, &buffer->dma_addr);
1266 : :
1267 : : /* We're done */
1268 : 0 : ioparam->handle = fd;
1269 : 0 : ioparam->vc_handle = buffer->vc_handle;
1270 : 0 : ioparam->dma_addr = buffer->dma_addr;
1271 : 0 : return 0;
1272 : :
1273 : : error:
1274 : 0 : pr_err("[%s]: something failed - cleanup. ret %d\n", __func__, ret);
1275 : :
1276 : 0 : if (dmabuf) {
1277 : : /* dmabuf has been exported, therefore allow dmabuf cleanup to
1278 : : * deal with this
1279 : : */
1280 : 0 : dma_buf_put(dmabuf);
1281 : : } else {
1282 : : /* No dmabuf, therefore just free the buffer here */
1283 : 0 : if (buffer->cookie)
1284 : 0 : dma_free_coherent(&sm_state->pdev->dev, buffer->size,
1285 : : buffer->cookie, buffer->dma_addr);
1286 : 0 : kfree(buffer);
1287 : : }
1288 : 0 : return ret;
1289 : : }
1290 : :
1291 : : #ifndef CONFIG_ARM64
1292 : : /* Converts VCSM_CACHE_OP_* to an operating function. */
1293 : 0 : static void (*cache_op_to_func(const unsigned int cache_op))
1294 : : (const void*, const void*)
1295 : : {
1296 : 0 : switch (cache_op) {
1297 : : case VC_SM_CACHE_OP_NOP:
1298 : : return NULL;
1299 : :
1300 : : case VC_SM_CACHE_OP_INV:
1301 : 0 : return dmac_inv_range;
1302 : :
1303 : : case VC_SM_CACHE_OP_CLEAN:
1304 : 0 : return dmac_clean_range;
1305 : :
1306 : : case VC_SM_CACHE_OP_FLUSH:
1307 : 0 : return dmac_flush_range;
1308 : :
1309 : : default:
1310 : 0 : pr_err("[%s]: Invalid cache_op: 0x%08x\n", __func__, cache_op);
1311 : 0 : return NULL;
1312 : : }
1313 : : }
1314 : :
1315 : : /*
1316 : : * Clean/invalid/flush cache of which buffer is already pinned (i.e. accessed).
1317 : : */
1318 : 0 : static int clean_invalid_contig_2d(const void __user *addr,
1319 : : const size_t block_count,
1320 : : const size_t block_size,
1321 : : const size_t stride,
1322 : : const unsigned int cache_op)
1323 : : {
1324 : : size_t i;
1325 : : void (*op_fn)(const void *start, const void *end);
1326 : :
1327 : 0 : if (!block_size) {
1328 : 0 : pr_err("[%s]: size cannot be 0\n", __func__);
1329 : 0 : return -EINVAL;
1330 : : }
1331 : :
1332 : 0 : op_fn = cache_op_to_func(cache_op);
1333 : 0 : if (!op_fn)
1334 : : return -EINVAL;
1335 : :
1336 : 0 : for (i = 0; i < block_count; i ++, addr += stride)
1337 : 0 : op_fn(addr, addr + block_size);
1338 : :
1339 : : return 0;
1340 : : }
1341 : :
1342 : 0 : static int vc_sm_cma_clean_invalid2(unsigned int cmdnr, unsigned long arg)
1343 : : {
1344 : : struct vc_sm_cma_ioctl_clean_invalid2 ioparam;
1345 : : struct vc_sm_cma_ioctl_clean_invalid_block *block = NULL;
1346 : : int i, ret = 0;
1347 : :
1348 : : /* Get parameter data. */
1349 : 0 : if (copy_from_user(&ioparam, (void *)arg, sizeof(ioparam))) {
1350 : 0 : pr_err("[%s]: failed to copy-from-user header for cmd %x\n",
1351 : : __func__, cmdnr);
1352 : 0 : return -EFAULT;
1353 : : }
1354 : 0 : block = kmalloc(ioparam.op_count * sizeof(*block), GFP_KERNEL);
1355 : 0 : if (!block)
1356 : : return -EFAULT;
1357 : :
1358 : 0 : if (copy_from_user(block, (void *)(arg + sizeof(ioparam)),
1359 : 0 : ioparam.op_count * sizeof(*block)) != 0) {
1360 : 0 : pr_err("[%s]: failed to copy-from-user payload for cmd %x\n",
1361 : : __func__, cmdnr);
1362 : : ret = -EFAULT;
1363 : 0 : goto out;
1364 : : }
1365 : :
1366 : 0 : for (i = 0; i < ioparam.op_count; i++) {
1367 : 0 : const struct vc_sm_cma_ioctl_clean_invalid_block * const op =
1368 : 0 : block + i;
1369 : :
1370 : 0 : if (op->invalidate_mode == VC_SM_CACHE_OP_NOP)
1371 : 0 : continue;
1372 : :
1373 : 0 : ret = clean_invalid_contig_2d((void __user *)op->start_address,
1374 : : op->block_count, op->block_size,
1375 : : op->inter_block_stride,
1376 : : op->invalidate_mode);
1377 : 0 : if (ret)
1378 : : break;
1379 : : }
1380 : : out:
1381 : 0 : kfree(block);
1382 : :
1383 : 0 : return ret;
1384 : : }
1385 : : #endif
1386 : :
1387 : 0 : static long vc_sm_cma_ioctl(struct file *file, unsigned int cmd,
1388 : : unsigned long arg)
1389 : : {
1390 : : int ret = 0;
1391 : 0 : unsigned int cmdnr = _IOC_NR(cmd);
1392 : 0 : struct vc_sm_privdata_t *file_data =
1393 : : (struct vc_sm_privdata_t *)file->private_data;
1394 : :
1395 : : /* Validate we can work with this device. */
1396 : 0 : if (!sm_state || !file_data) {
1397 : 0 : pr_err("[%s]: invalid device\n", __func__);
1398 : 0 : return -EPERM;
1399 : : }
1400 : :
1401 : : /* Action is a re-post of a previously interrupted action? */
1402 : 0 : if (file_data->restart_sys == -EINTR) {
1403 : : struct vc_sm_action_clean_t action_clean;
1404 : :
1405 : : pr_debug("[%s]: clean up of action %u (trans_id: %u) following EINTR\n",
1406 : : __func__, file_data->int_action,
1407 : : file_data->int_trans_id);
1408 : :
1409 : : action_clean.res_action = file_data->int_action;
1410 : : action_clean.action_trans_id = file_data->int_trans_id;
1411 : :
1412 : 0 : file_data->restart_sys = 0;
1413 : : }
1414 : :
1415 : : /* Now process the command. */
1416 : 0 : switch (cmdnr) {
1417 : : /* New memory allocation.
1418 : : */
1419 : : case VC_SM_CMA_CMD_ALLOC:
1420 : : {
1421 : : struct vc_sm_cma_ioctl_alloc ioparam;
1422 : :
1423 : : /* Get the parameter data. */
1424 : 0 : if (copy_from_user
1425 : : (&ioparam, (void *)arg, sizeof(ioparam)) != 0) {
1426 : 0 : pr_err("[%s]: failed to copy-from-user for cmd %x\n",
1427 : : __func__, cmdnr);
1428 : : ret = -EFAULT;
1429 : 0 : break;
1430 : : }
1431 : :
1432 : 0 : ret = vc_sm_cma_ioctl_alloc(file_data, &ioparam);
1433 : 0 : if (!ret &&
1434 : 0 : (copy_to_user((void *)arg, &ioparam,
1435 : : sizeof(ioparam)) != 0)) {
1436 : : /* FIXME: Release allocation */
1437 : 0 : pr_err("[%s]: failed to copy-to-user for cmd %x\n",
1438 : : __func__, cmdnr);
1439 : : ret = -EFAULT;
1440 : : }
1441 : : break;
1442 : : }
1443 : :
1444 : : case VC_SM_CMA_CMD_IMPORT_DMABUF:
1445 : : {
1446 : : struct vc_sm_cma_ioctl_import_dmabuf ioparam;
1447 : : struct dma_buf *new_dmabuf;
1448 : :
1449 : : /* Get the parameter data. */
1450 : 0 : if (copy_from_user
1451 : : (&ioparam, (void *)arg, sizeof(ioparam)) != 0) {
1452 : 0 : pr_err("[%s]: failed to copy-from-user for cmd %x\n",
1453 : : __func__, cmdnr);
1454 : : ret = -EFAULT;
1455 : 0 : break;
1456 : : }
1457 : :
1458 : 0 : ret = vc_sm_cma_import_dmabuf_internal(file_data,
1459 : : NULL,
1460 : : ioparam.dmabuf_fd,
1461 : : &new_dmabuf);
1462 : :
1463 : 0 : if (!ret) {
1464 : 0 : struct vc_sm_buffer *buf = new_dmabuf->priv;
1465 : :
1466 : 0 : ioparam.size = buf->size;
1467 : 0 : ioparam.handle = dma_buf_fd(new_dmabuf,
1468 : : O_CLOEXEC);
1469 : 0 : ioparam.vc_handle = buf->vc_handle;
1470 : 0 : ioparam.dma_addr = buf->dma_addr;
1471 : :
1472 : 0 : if (ioparam.handle < 0 ||
1473 : 0 : (copy_to_user((void *)arg, &ioparam,
1474 : : sizeof(ioparam)) != 0)) {
1475 : 0 : dma_buf_put(new_dmabuf);
1476 : : /* FIXME: Release allocation */
1477 : : ret = -EFAULT;
1478 : : }
1479 : : }
1480 : : break;
1481 : : }
1482 : :
1483 : : #ifndef CONFIG_ARM64
1484 : : /*
1485 : : * Flush/Invalidate the cache for a given mapping.
1486 : : * Blocks must be pinned (i.e. accessed) before this call.
1487 : : */
1488 : : case VC_SM_CMA_CMD_CLEAN_INVALID2:
1489 : 0 : ret = vc_sm_cma_clean_invalid2(cmdnr, arg);
1490 : 0 : break;
1491 : : #endif
1492 : :
1493 : : default:
1494 : : pr_debug("[%s]: cmd %x tgid %u, owner %u\n", __func__, cmdnr,
1495 : : current->tgid, file_data->pid);
1496 : :
1497 : : ret = -EINVAL;
1498 : : break;
1499 : : }
1500 : :
1501 : 0 : return ret;
1502 : : }
1503 : :
1504 : : #ifdef CONFIG_COMPAT
1505 : : struct vc_sm_cma_ioctl_clean_invalid2_32 {
1506 : : u32 op_count;
1507 : : struct vc_sm_cma_ioctl_clean_invalid_block_32 {
1508 : : u16 invalidate_mode;
1509 : : u16 block_count;
1510 : : compat_uptr_t start_address;
1511 : : u32 block_size;
1512 : : u32 inter_block_stride;
1513 : : } s[0];
1514 : : };
1515 : :
1516 : : #define VC_SM_CMA_CMD_CLEAN_INVALID2_32\
1517 : : _IOR(VC_SM_CMA_MAGIC_TYPE, VC_SM_CMA_CMD_CLEAN_INVALID2,\
1518 : : struct vc_sm_cma_ioctl_clean_invalid2_32)
1519 : :
1520 : : static long vc_sm_cma_compat_ioctl(struct file *file, unsigned int cmd,
1521 : : unsigned long arg)
1522 : : {
1523 : : switch (cmd) {
1524 : : case VC_SM_CMA_CMD_CLEAN_INVALID2_32:
1525 : : /* FIXME */
1526 : : return -EINVAL;
1527 : :
1528 : : default:
1529 : : return vc_sm_cma_ioctl(file, cmd, arg);
1530 : : }
1531 : : }
1532 : : #endif
1533 : :
1534 : : /* Device operations that we managed in this driver. */
1535 : : static const struct file_operations vc_sm_ops = {
1536 : : .owner = THIS_MODULE,
1537 : : .unlocked_ioctl = vc_sm_cma_ioctl,
1538 : : #ifdef CONFIG_COMPAT
1539 : : .compat_ioctl = vc_sm_cma_compat_ioctl,
1540 : : #endif
1541 : : .open = vc_sm_cma_open,
1542 : : .release = vc_sm_cma_release,
1543 : : };
1544 : :
1545 : : /* Driver load/unload functions */
1546 : : /* Videocore connected. */
1547 : 3 : static void vc_sm_connected_init(void)
1548 : : {
1549 : : int ret;
1550 : : VCHI_INSTANCE_T vchi_instance;
1551 : : struct vc_sm_version version;
1552 : : struct vc_sm_result_t version_result;
1553 : :
1554 : 3 : pr_info("[%s]: start\n", __func__);
1555 : :
1556 : : /*
1557 : : * Initialize and create a VCHI connection for the shared memory service
1558 : : * running on videocore.
1559 : : */
1560 : 3 : ret = vchi_initialise(&vchi_instance);
1561 : 3 : if (ret) {
1562 : 0 : pr_err("[%s]: failed to initialise VCHI instance (ret=%d)\n",
1563 : : __func__, ret);
1564 : :
1565 : 0 : return;
1566 : : }
1567 : :
1568 : 3 : ret = vchi_connect(vchi_instance);
1569 : 3 : if (ret) {
1570 : 0 : pr_err("[%s]: failed to connect VCHI instance (ret=%d)\n",
1571 : : __func__, ret);
1572 : :
1573 : 0 : return;
1574 : : }
1575 : :
1576 : : /* Initialize an instance of the shared memory service. */
1577 : 3 : sm_state->sm_handle = vc_sm_cma_vchi_init(vchi_instance, 1,
1578 : : vc_sm_vpu_event);
1579 : 3 : if (!sm_state->sm_handle) {
1580 : 0 : pr_err("[%s]: failed to initialize shared memory service\n",
1581 : : __func__);
1582 : :
1583 : 0 : return;
1584 : : }
1585 : :
1586 : : /* Create a debug fs directory entry (root). */
1587 : 3 : sm_state->dir_root = debugfs_create_dir(VC_SM_DIR_ROOT_NAME, NULL);
1588 : :
1589 : 3 : sm_state->dir_state.show = &vc_sm_cma_global_state_show;
1590 : 3 : sm_state->dir_state.dir_entry =
1591 : 3 : debugfs_create_file(VC_SM_STATE, 0444, sm_state->dir_root,
1592 : 3 : &sm_state->dir_state,
1593 : : &vc_sm_cma_debug_fs_fops);
1594 : :
1595 : 3 : INIT_LIST_HEAD(&sm_state->buffer_list);
1596 : :
1597 : : /* Create a shared memory device. */
1598 : 3 : sm_state->misc_dev.minor = MISC_DYNAMIC_MINOR;
1599 : 3 : sm_state->misc_dev.name = DEVICE_NAME;
1600 : 3 : sm_state->misc_dev.fops = &vc_sm_ops;
1601 : 3 : sm_state->misc_dev.parent = NULL;
1602 : : /* Temporarily set as 666 until udev rules have been sorted */
1603 : 3 : sm_state->misc_dev.mode = 0666;
1604 : 3 : ret = misc_register(&sm_state->misc_dev);
1605 : 3 : if (ret) {
1606 : 0 : pr_err("vcsm-cma: failed to register misc device.\n");
1607 : 0 : goto err_remove_debugfs;
1608 : : }
1609 : :
1610 : 3 : sm_state->data_knl = vc_sm_cma_create_priv_data(0);
1611 : 3 : if (!sm_state->data_knl) {
1612 : 0 : pr_err("[%s]: failed to create kernel private data tracker\n",
1613 : : __func__);
1614 : : goto err_remove_misc_dev;
1615 : : }
1616 : :
1617 : 3 : version.version = 2;
1618 : 3 : ret = vc_sm_cma_vchi_client_version(sm_state->sm_handle, &version,
1619 : : &version_result,
1620 : : &sm_state->int_trans_id);
1621 : 3 : if (ret) {
1622 : 0 : pr_err("[%s]: Failed to send version request %d\n", __func__,
1623 : : ret);
1624 : : }
1625 : :
1626 : : /* Done! */
1627 : 3 : sm_inited = 1;
1628 : 3 : pr_info("[%s]: installed successfully\n", __func__);
1629 : 3 : return;
1630 : :
1631 : : err_remove_misc_dev:
1632 : 0 : misc_deregister(&sm_state->misc_dev);
1633 : : err_remove_debugfs:
1634 : 0 : debugfs_remove_recursive(sm_state->dir_root);
1635 : 0 : vc_sm_cma_vchi_stop(&sm_state->sm_handle);
1636 : : }
1637 : :
1638 : : /* Driver loading. */
1639 : 3 : static int bcm2835_vc_sm_cma_probe(struct platform_device *pdev)
1640 : : {
1641 : 3 : pr_info("%s: Videocore shared memory driver\n", __func__);
1642 : :
1643 : 3 : sm_state = devm_kzalloc(&pdev->dev, sizeof(*sm_state), GFP_KERNEL);
1644 : 3 : if (!sm_state)
1645 : : return -ENOMEM;
1646 : 3 : sm_state->pdev = pdev;
1647 : 3 : mutex_init(&sm_state->map_lock);
1648 : :
1649 : 3 : spin_lock_init(&sm_state->kernelid_map_lock);
1650 : : idr_init_base(&sm_state->kernelid_map, 1);
1651 : :
1652 : 3 : pdev->dev.dma_parms = devm_kzalloc(&pdev->dev,
1653 : : sizeof(*pdev->dev.dma_parms),
1654 : : GFP_KERNEL);
1655 : : /* dma_set_max_seg_size checks if dma_parms is NULL. */
1656 : : dma_set_max_seg_size(&pdev->dev, 0x3FFFFFFF);
1657 : :
1658 : 3 : vchiq_add_connected_callback(vc_sm_connected_init);
1659 : 3 : return 0;
1660 : : }
1661 : :
1662 : : /* Driver unloading. */
1663 : 0 : static int bcm2835_vc_sm_cma_remove(struct platform_device *pdev)
1664 : : {
1665 : : pr_debug("[%s]: start\n", __func__);
1666 : 0 : if (sm_inited) {
1667 : 0 : misc_deregister(&sm_state->misc_dev);
1668 : :
1669 : : /* Remove all proc entries. */
1670 : 0 : debugfs_remove_recursive(sm_state->dir_root);
1671 : :
1672 : : /* Stop the videocore shared memory service. */
1673 : 0 : vc_sm_cma_vchi_stop(&sm_state->sm_handle);
1674 : : }
1675 : :
1676 : 0 : if (sm_state) {
1677 : 0 : idr_destroy(&sm_state->kernelid_map);
1678 : :
1679 : : /* Free the memory for the state structure. */
1680 : : mutex_destroy(&sm_state->map_lock);
1681 : : }
1682 : :
1683 : : pr_debug("[%s]: end\n", __func__);
1684 : 0 : return 0;
1685 : : }
1686 : :
1687 : : /* Kernel API calls */
1688 : : /* Get an internal resource handle mapped from the external one. */
1689 : 0 : int vc_sm_cma_int_handle(void *handle)
1690 : : {
1691 : : struct dma_buf *dma_buf = (struct dma_buf *)handle;
1692 : : struct vc_sm_buffer *buf;
1693 : :
1694 : : /* Validate we can work with this device. */
1695 : 0 : if (!sm_state || !handle) {
1696 : 0 : pr_err("[%s]: invalid input\n", __func__);
1697 : 0 : return 0;
1698 : : }
1699 : :
1700 : 0 : buf = (struct vc_sm_buffer *)dma_buf->priv;
1701 : 0 : return buf->vc_handle;
1702 : : }
1703 : : EXPORT_SYMBOL_GPL(vc_sm_cma_int_handle);
1704 : :
1705 : : /* Free a previously allocated shared memory handle and block. */
1706 : 0 : int vc_sm_cma_free(void *handle)
1707 : : {
1708 : : struct dma_buf *dma_buf = (struct dma_buf *)handle;
1709 : :
1710 : : /* Validate we can work with this device. */
1711 : 0 : if (!sm_state || !handle) {
1712 : 0 : pr_err("[%s]: invalid input\n", __func__);
1713 : 0 : return -EPERM;
1714 : : }
1715 : :
1716 : : pr_debug("%s: handle %p/dmabuf %p\n", __func__, handle, dma_buf);
1717 : :
1718 : 0 : dma_buf_put(dma_buf);
1719 : :
1720 : 0 : return 0;
1721 : : }
1722 : : EXPORT_SYMBOL_GPL(vc_sm_cma_free);
1723 : :
1724 : : /* Import a dmabuf to be shared with VC. */
1725 : 0 : int vc_sm_cma_import_dmabuf(struct dma_buf *src_dmabuf, void **handle)
1726 : : {
1727 : : struct dma_buf *new_dma_buf;
1728 : : struct vc_sm_buffer *buf;
1729 : : int ret;
1730 : :
1731 : : /* Validate we can work with this device. */
1732 : 0 : if (!sm_state || !src_dmabuf || !handle) {
1733 : 0 : pr_err("[%s]: invalid input\n", __func__);
1734 : 0 : return -EPERM;
1735 : : }
1736 : :
1737 : 0 : ret = vc_sm_cma_import_dmabuf_internal(sm_state->data_knl, src_dmabuf,
1738 : : -1, &new_dma_buf);
1739 : :
1740 : 0 : if (!ret) {
1741 : : pr_debug("%s: imported to ptr %p\n", __func__, new_dma_buf);
1742 : : buf = (struct vc_sm_buffer *)new_dma_buf->priv;
1743 : :
1744 : : /* Assign valid handle at this time.*/
1745 : 0 : *handle = new_dma_buf;
1746 : : } else {
1747 : : /*
1748 : : * succeeded in importing the dma_buf, but then
1749 : : * failed to look it up again. How?
1750 : : * Release the fd again.
1751 : : */
1752 : 0 : pr_err("%s: imported vc_sm_cma_get_buffer failed %d\n",
1753 : : __func__, ret);
1754 : : }
1755 : :
1756 : 0 : return ret;
1757 : : }
1758 : : EXPORT_SYMBOL_GPL(vc_sm_cma_import_dmabuf);
1759 : :
1760 : : static struct platform_driver bcm2835_vcsm_cma_driver = {
1761 : : .probe = bcm2835_vc_sm_cma_probe,
1762 : : .remove = bcm2835_vc_sm_cma_remove,
1763 : : .driver = {
1764 : : .name = DEVICE_NAME,
1765 : : .owner = THIS_MODULE,
1766 : : },
1767 : : };
1768 : :
1769 : 3 : module_platform_driver(bcm2835_vcsm_cma_driver);
1770 : :
1771 : : MODULE_AUTHOR("Dave Stevenson");
1772 : : MODULE_DESCRIPTION("VideoCore CMA Shared Memory Driver");
1773 : : MODULE_LICENSE("GPL v2");
1774 : : MODULE_ALIAS("platform:vcsm-cma");
|