18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * helper functions for physically contiguous capture buffers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The functions support hardware lacking scatter gather support 68c2ecf20Sopenharmony_ci * (i.e. the buffers must be linear in physical memory) 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (c) 2008 Magnus Damm 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on videobuf-vmalloc.c, 118c2ecf20Sopenharmony_ci * (c) 2007 Mauro Carvalho Chehab, <mchehab@kernel.org> 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/init.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include <linux/mm.h> 178c2ecf20Sopenharmony_ci#include <linux/pagemap.h> 188c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 198c2ecf20Sopenharmony_ci#include <linux/sched.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci#include <media/videobuf-dma-contig.h> 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistruct videobuf_dma_contig_memory { 248c2ecf20Sopenharmony_ci u32 magic; 258c2ecf20Sopenharmony_ci void *vaddr; 268c2ecf20Sopenharmony_ci dma_addr_t dma_handle; 278c2ecf20Sopenharmony_ci unsigned long size; 288c2ecf20Sopenharmony_ci}; 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define MAGIC_DC_MEM 0x0733ac61 318c2ecf20Sopenharmony_ci#define MAGIC_CHECK(is, should) \ 328c2ecf20Sopenharmony_ci if (unlikely((is) != (should))) { \ 338c2ecf20Sopenharmony_ci pr_err("magic mismatch: %x expected %x\n", (is), (should)); \ 348c2ecf20Sopenharmony_ci BUG(); \ 358c2ecf20Sopenharmony_ci } 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic int __videobuf_dc_alloc(struct device *dev, 388c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem, 398c2ecf20Sopenharmony_ci unsigned long size) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci mem->size = size; 428c2ecf20Sopenharmony_ci mem->vaddr = dma_alloc_coherent(dev, mem->size, &mem->dma_handle, 438c2ecf20Sopenharmony_ci GFP_KERNEL); 448c2ecf20Sopenharmony_ci if (!mem->vaddr) { 458c2ecf20Sopenharmony_ci dev_err(dev, "memory alloc size %ld failed\n", mem->size); 468c2ecf20Sopenharmony_ci return -ENOMEM; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci return 0; 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic void __videobuf_dc_free(struct device *dev, 558c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci mem->vaddr = NULL; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic void videobuf_vm_open(struct vm_area_struct *vma) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct videobuf_mapping *map = vma->vm_private_data; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci dev_dbg(map->q->dev, "vm_open %p [count=%u,vma=%08lx-%08lx]\n", 678c2ecf20Sopenharmony_ci map, map->count, vma->vm_start, vma->vm_end); 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci map->count++; 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void videobuf_vm_close(struct vm_area_struct *vma) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct videobuf_mapping *map = vma->vm_private_data; 758c2ecf20Sopenharmony_ci struct videobuf_queue *q = map->q; 768c2ecf20Sopenharmony_ci int i; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci dev_dbg(q->dev, "vm_close %p [count=%u,vma=%08lx-%08lx]\n", 798c2ecf20Sopenharmony_ci map, map->count, vma->vm_start, vma->vm_end); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci map->count--; 828c2ecf20Sopenharmony_ci if (0 == map->count) { 838c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci dev_dbg(q->dev, "munmap %p q=%p\n", map, q); 868c2ecf20Sopenharmony_ci videobuf_queue_lock(q); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci /* We need first to cancel streams, before unmapping */ 898c2ecf20Sopenharmony_ci if (q->streaming) 908c2ecf20Sopenharmony_ci videobuf_queue_cancel(q); 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci for (i = 0; i < VIDEO_MAX_FRAME; i++) { 938c2ecf20Sopenharmony_ci if (NULL == q->bufs[i]) 948c2ecf20Sopenharmony_ci continue; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci if (q->bufs[i]->map != map) 978c2ecf20Sopenharmony_ci continue; 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci mem = q->bufs[i]->priv; 1008c2ecf20Sopenharmony_ci if (mem) { 1018c2ecf20Sopenharmony_ci /* This callback is called only if kernel has 1028c2ecf20Sopenharmony_ci allocated memory and this memory is mmapped. 1038c2ecf20Sopenharmony_ci In this case, memory should be freed, 1048c2ecf20Sopenharmony_ci in order to do memory unmap. 1058c2ecf20Sopenharmony_ci */ 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci /* vfree is not atomic - can't be 1108c2ecf20Sopenharmony_ci called with IRQ's disabled 1118c2ecf20Sopenharmony_ci */ 1128c2ecf20Sopenharmony_ci dev_dbg(q->dev, "buf[%d] freeing %p\n", 1138c2ecf20Sopenharmony_ci i, mem->vaddr); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci __videobuf_dc_free(q->dev, mem); 1168c2ecf20Sopenharmony_ci mem->vaddr = NULL; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci q->bufs[i]->map = NULL; 1208c2ecf20Sopenharmony_ci q->bufs[i]->baddr = 0; 1218c2ecf20Sopenharmony_ci } 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci kfree(map); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci videobuf_queue_unlock(q); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct vm_operations_struct videobuf_vm_ops = { 1308c2ecf20Sopenharmony_ci .open = videobuf_vm_open, 1318c2ecf20Sopenharmony_ci .close = videobuf_vm_close, 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/** 1358c2ecf20Sopenharmony_ci * videobuf_dma_contig_user_put() - reset pointer to user space buffer 1368c2ecf20Sopenharmony_ci * @mem: per-buffer private videobuf-dma-contig data 1378c2ecf20Sopenharmony_ci * 1388c2ecf20Sopenharmony_ci * This function resets the user space pointer 1398c2ecf20Sopenharmony_ci */ 1408c2ecf20Sopenharmony_cistatic void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem) 1418c2ecf20Sopenharmony_ci{ 1428c2ecf20Sopenharmony_ci mem->dma_handle = 0; 1438c2ecf20Sopenharmony_ci mem->size = 0; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci/** 1478c2ecf20Sopenharmony_ci * videobuf_dma_contig_user_get() - setup user space memory pointer 1488c2ecf20Sopenharmony_ci * @mem: per-buffer private videobuf-dma-contig data 1498c2ecf20Sopenharmony_ci * @vb: video buffer to map 1508c2ecf20Sopenharmony_ci * 1518c2ecf20Sopenharmony_ci * This function validates and sets up a pointer to user space memory. 1528c2ecf20Sopenharmony_ci * Only physically contiguous pfn-mapped memory is accepted. 1538c2ecf20Sopenharmony_ci * 1548c2ecf20Sopenharmony_ci * Returns 0 if successful. 1558c2ecf20Sopenharmony_ci */ 1568c2ecf20Sopenharmony_cistatic int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, 1578c2ecf20Sopenharmony_ci struct videobuf_buffer *vb) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci unsigned long untagged_baddr = untagged_addr(vb->baddr); 1608c2ecf20Sopenharmony_ci struct mm_struct *mm = current->mm; 1618c2ecf20Sopenharmony_ci struct vm_area_struct *vma; 1628c2ecf20Sopenharmony_ci unsigned long prev_pfn, this_pfn; 1638c2ecf20Sopenharmony_ci unsigned long pages_done, user_address; 1648c2ecf20Sopenharmony_ci unsigned int offset; 1658c2ecf20Sopenharmony_ci int ret; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci offset = untagged_baddr & ~PAGE_MASK; 1688c2ecf20Sopenharmony_ci mem->size = PAGE_ALIGN(vb->size + offset); 1698c2ecf20Sopenharmony_ci ret = -EINVAL; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci mmap_read_lock(mm); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci vma = find_vma(mm, untagged_baddr); 1748c2ecf20Sopenharmony_ci if (!vma) 1758c2ecf20Sopenharmony_ci goto out_up; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if ((untagged_baddr + mem->size) > vma->vm_end) 1788c2ecf20Sopenharmony_ci goto out_up; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci pages_done = 0; 1818c2ecf20Sopenharmony_ci prev_pfn = 0; /* kill warning */ 1828c2ecf20Sopenharmony_ci user_address = untagged_baddr; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci while (pages_done < (mem->size >> PAGE_SHIFT)) { 1858c2ecf20Sopenharmony_ci ret = follow_pfn(vma, user_address, &this_pfn); 1868c2ecf20Sopenharmony_ci if (ret) 1878c2ecf20Sopenharmony_ci break; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci if (pages_done == 0) 1908c2ecf20Sopenharmony_ci mem->dma_handle = (this_pfn << PAGE_SHIFT) + offset; 1918c2ecf20Sopenharmony_ci else if (this_pfn != (prev_pfn + 1)) 1928c2ecf20Sopenharmony_ci ret = -EFAULT; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (ret) 1958c2ecf20Sopenharmony_ci break; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci prev_pfn = this_pfn; 1988c2ecf20Sopenharmony_ci user_address += PAGE_SIZE; 1998c2ecf20Sopenharmony_ci pages_done++; 2008c2ecf20Sopenharmony_ci } 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ciout_up: 2038c2ecf20Sopenharmony_ci mmap_read_unlock(current->mm); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci return ret; 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic struct videobuf_buffer *__videobuf_alloc(size_t size) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem; 2118c2ecf20Sopenharmony_ci struct videobuf_buffer *vb; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); 2148c2ecf20Sopenharmony_ci if (vb) { 2158c2ecf20Sopenharmony_ci vb->priv = ((char *)vb) + size; 2168c2ecf20Sopenharmony_ci mem = vb->priv; 2178c2ecf20Sopenharmony_ci mem->magic = MAGIC_DC_MEM; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci return vb; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic void *__videobuf_to_vaddr(struct videobuf_buffer *buf) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem = buf->priv; 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci BUG_ON(!mem); 2288c2ecf20Sopenharmony_ci MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci return mem->vaddr; 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic int __videobuf_iolock(struct videobuf_queue *q, 2348c2ecf20Sopenharmony_ci struct videobuf_buffer *vb, 2358c2ecf20Sopenharmony_ci struct v4l2_framebuffer *fbuf) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem = vb->priv; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci BUG_ON(!mem); 2408c2ecf20Sopenharmony_ci MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci switch (vb->memory) { 2438c2ecf20Sopenharmony_ci case V4L2_MEMORY_MMAP: 2448c2ecf20Sopenharmony_ci dev_dbg(q->dev, "%s memory method MMAP\n", __func__); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* All handling should be done by __videobuf_mmap_mapper() */ 2478c2ecf20Sopenharmony_ci if (!mem->vaddr) { 2488c2ecf20Sopenharmony_ci dev_err(q->dev, "memory is not allocated/mmapped.\n"); 2498c2ecf20Sopenharmony_ci return -EINVAL; 2508c2ecf20Sopenharmony_ci } 2518c2ecf20Sopenharmony_ci break; 2528c2ecf20Sopenharmony_ci case V4L2_MEMORY_USERPTR: 2538c2ecf20Sopenharmony_ci dev_dbg(q->dev, "%s memory method USERPTR\n", __func__); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci /* handle pointer from user space */ 2568c2ecf20Sopenharmony_ci if (vb->baddr) 2578c2ecf20Sopenharmony_ci return videobuf_dma_contig_user_get(mem, vb); 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci /* allocate memory for the read() method */ 2608c2ecf20Sopenharmony_ci if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size))) 2618c2ecf20Sopenharmony_ci return -ENOMEM; 2628c2ecf20Sopenharmony_ci break; 2638c2ecf20Sopenharmony_ci case V4L2_MEMORY_OVERLAY: 2648c2ecf20Sopenharmony_ci default: 2658c2ecf20Sopenharmony_ci dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__); 2668c2ecf20Sopenharmony_ci return -EINVAL; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci return 0; 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_cistatic int __videobuf_mmap_mapper(struct videobuf_queue *q, 2738c2ecf20Sopenharmony_ci struct videobuf_buffer *buf, 2748c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem; 2778c2ecf20Sopenharmony_ci struct videobuf_mapping *map; 2788c2ecf20Sopenharmony_ci int retval; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dev_dbg(q->dev, "%s\n", __func__); 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci /* create mapping + update buffer list */ 2838c2ecf20Sopenharmony_ci map = kzalloc(sizeof(struct videobuf_mapping), GFP_KERNEL); 2848c2ecf20Sopenharmony_ci if (!map) 2858c2ecf20Sopenharmony_ci return -ENOMEM; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci buf->map = map; 2888c2ecf20Sopenharmony_ci map->q = q; 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci buf->baddr = vma->vm_start; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci mem = buf->priv; 2938c2ecf20Sopenharmony_ci BUG_ON(!mem); 2948c2ecf20Sopenharmony_ci MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize))) 2978c2ecf20Sopenharmony_ci goto error; 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci /* the "vm_pgoff" is just used in v4l2 to find the 3008c2ecf20Sopenharmony_ci * corresponding buffer data structure which is allocated 3018c2ecf20Sopenharmony_ci * earlier and it does not mean the offset from the physical 3028c2ecf20Sopenharmony_ci * buffer start address as usual. So set it to 0 to pass 3038c2ecf20Sopenharmony_ci * the sanity check in dma_mmap_coherent(). 3048c2ecf20Sopenharmony_ci */ 3058c2ecf20Sopenharmony_ci vma->vm_pgoff = 0; 3068c2ecf20Sopenharmony_ci retval = dma_mmap_coherent(q->dev, vma, mem->vaddr, mem->dma_handle, 3078c2ecf20Sopenharmony_ci mem->size); 3088c2ecf20Sopenharmony_ci if (retval) { 3098c2ecf20Sopenharmony_ci dev_err(q->dev, "mmap: remap failed with error %d. ", 3108c2ecf20Sopenharmony_ci retval); 3118c2ecf20Sopenharmony_ci dma_free_coherent(q->dev, mem->size, 3128c2ecf20Sopenharmony_ci mem->vaddr, mem->dma_handle); 3138c2ecf20Sopenharmony_ci goto error; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci vma->vm_ops = &videobuf_vm_ops; 3178c2ecf20Sopenharmony_ci vma->vm_flags |= VM_DONTEXPAND; 3188c2ecf20Sopenharmony_ci vma->vm_private_data = map; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", 3218c2ecf20Sopenharmony_ci map, q, vma->vm_start, vma->vm_end, 3228c2ecf20Sopenharmony_ci (long int)buf->bsize, vma->vm_pgoff, buf->i); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci videobuf_vm_open(vma); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci return 0; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cierror: 3298c2ecf20Sopenharmony_ci kfree(map); 3308c2ecf20Sopenharmony_ci return -ENOMEM; 3318c2ecf20Sopenharmony_ci} 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_cistatic struct videobuf_qtype_ops qops = { 3348c2ecf20Sopenharmony_ci .magic = MAGIC_QTYPE_OPS, 3358c2ecf20Sopenharmony_ci .alloc_vb = __videobuf_alloc, 3368c2ecf20Sopenharmony_ci .iolock = __videobuf_iolock, 3378c2ecf20Sopenharmony_ci .mmap_mapper = __videobuf_mmap_mapper, 3388c2ecf20Sopenharmony_ci .vaddr = __videobuf_to_vaddr, 3398c2ecf20Sopenharmony_ci}; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_civoid videobuf_queue_dma_contig_init(struct videobuf_queue *q, 3428c2ecf20Sopenharmony_ci const struct videobuf_queue_ops *ops, 3438c2ecf20Sopenharmony_ci struct device *dev, 3448c2ecf20Sopenharmony_ci spinlock_t *irqlock, 3458c2ecf20Sopenharmony_ci enum v4l2_buf_type type, 3468c2ecf20Sopenharmony_ci enum v4l2_field field, 3478c2ecf20Sopenharmony_ci unsigned int msize, 3488c2ecf20Sopenharmony_ci void *priv, 3498c2ecf20Sopenharmony_ci struct mutex *ext_lock) 3508c2ecf20Sopenharmony_ci{ 3518c2ecf20Sopenharmony_ci videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, 3528c2ecf20Sopenharmony_ci priv, &qops, ext_lock); 3538c2ecf20Sopenharmony_ci} 3548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_cidma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem = buf->priv; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci BUG_ON(!mem); 3618c2ecf20Sopenharmony_ci MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci return mem->dma_handle; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(videobuf_to_dma_contig); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_civoid videobuf_dma_contig_free(struct videobuf_queue *q, 3688c2ecf20Sopenharmony_ci struct videobuf_buffer *buf) 3698c2ecf20Sopenharmony_ci{ 3708c2ecf20Sopenharmony_ci struct videobuf_dma_contig_memory *mem = buf->priv; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci /* mmapped memory can't be freed here, otherwise mmapped region 3738c2ecf20Sopenharmony_ci would be released, while still needed. In this case, the memory 3748c2ecf20Sopenharmony_ci release should happen inside videobuf_vm_close(). 3758c2ecf20Sopenharmony_ci So, it should free memory only if the memory were allocated for 3768c2ecf20Sopenharmony_ci read() operation. 3778c2ecf20Sopenharmony_ci */ 3788c2ecf20Sopenharmony_ci if (buf->memory != V4L2_MEMORY_USERPTR) 3798c2ecf20Sopenharmony_ci return; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!mem) 3828c2ecf20Sopenharmony_ci return; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* handle user space pointer case */ 3878c2ecf20Sopenharmony_ci if (buf->baddr) { 3888c2ecf20Sopenharmony_ci videobuf_dma_contig_user_put(mem); 3898c2ecf20Sopenharmony_ci return; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci /* read() method */ 3938c2ecf20Sopenharmony_ci if (mem->vaddr) { 3948c2ecf20Sopenharmony_ci __videobuf_dc_free(q->dev, mem); 3958c2ecf20Sopenharmony_ci mem->vaddr = NULL; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(videobuf_dma_contig_free); 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("helper module to manage video4linux dma contig buffers"); 4018c2ecf20Sopenharmony_ciMODULE_AUTHOR("Magnus Damm"); 4028c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 403