2010-10-11 17:58:53 +04:00
/*
* videobuf2 - vmalloc . c - vmalloc memory allocator for videobuf2
*
* Copyright ( C ) 2010 Samsung Electronics
*
2011-03-13 21:23:32 +03:00
* Author : Pawel Osciak < pawel @ osciak . com >
2010-10-11 17:58:53 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation .
*/
2012-02-16 19:19:08 +04:00
# include <linux/io.h>
2010-10-11 17:58:53 +04:00
# include <linux/module.h>
# include <linux/mm.h>
2011-10-13 14:30:51 +04:00
# include <linux/sched.h>
2010-10-11 17:58:53 +04:00
# include <linux/slab.h>
# include <linux/vmalloc.h>
# include <media/videobuf2-core.h>
2012-08-03 14:23:54 +04:00
# include <media/videobuf2-vmalloc.h>
2010-10-11 17:58:53 +04:00
# include <media/videobuf2-memops.h>
struct vb2_vmalloc_buf {
void * vaddr ;
2011-10-13 14:30:51 +04:00
struct page * * pages ;
2012-02-16 19:19:08 +04:00
struct vm_area_struct * vma ;
2014-11-18 15:50:58 +03:00
enum dma_data_direction dma_dir ;
2010-10-11 17:58:53 +04:00
unsigned long size ;
2011-10-13 14:30:51 +04:00
unsigned int n_pages ;
2010-10-11 17:58:53 +04:00
atomic_t refcount ;
struct vb2_vmarea_handler handler ;
2012-06-14 17:37:46 +04:00
struct dma_buf * dbuf ;
2010-10-11 17:58:53 +04:00
} ;
static void vb2_vmalloc_put ( void * buf_priv ) ;
2013-03-01 22:44:20 +04:00
static void * vb2_vmalloc_alloc ( void * alloc_ctx , unsigned long size , gfp_t gfp_flags )
2010-10-11 17:58:53 +04:00
{
struct vb2_vmalloc_buf * buf ;
2013-03-01 22:44:20 +04:00
buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL | gfp_flags ) ;
2010-10-11 17:58:53 +04:00
if ( ! buf )
return NULL ;
buf - > size = size ;
buf - > vaddr = vmalloc_user ( buf - > size ) ;
buf - > handler . refcount = & buf - > refcount ;
buf - > handler . put = vb2_vmalloc_put ;
buf - > handler . arg = buf ;
if ( ! buf - > vaddr ) {
2011-10-13 14:30:51 +04:00
pr_debug ( " vmalloc of size %ld failed \n " , buf - > size ) ;
2010-10-11 17:58:53 +04:00
kfree ( buf ) ;
return NULL ;
}
atomic_inc ( & buf - > refcount ) ;
return buf ;
}
static void vb2_vmalloc_put ( void * buf_priv )
{
struct vb2_vmalloc_buf * buf = buf_priv ;
if ( atomic_dec_and_test ( & buf - > refcount ) ) {
vfree ( buf - > vaddr ) ;
kfree ( buf ) ;
}
}
2011-10-13 14:30:51 +04:00
static void * vb2_vmalloc_get_userptr ( void * alloc_ctx , unsigned long vaddr ,
2014-11-18 15:50:58 +03:00
unsigned long size ,
enum dma_data_direction dma_dir )
2011-10-13 14:30:51 +04:00
{
struct vb2_vmalloc_buf * buf ;
unsigned long first , last ;
int n_pages , offset ;
2012-02-16 19:19:08 +04:00
struct vm_area_struct * vma ;
dma_addr_t physp ;
2011-10-13 14:30:51 +04:00
buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL ) ;
if ( ! buf )
return NULL ;
2014-11-18 15:50:58 +03:00
buf - > dma_dir = dma_dir ;
2011-10-13 14:30:51 +04:00
offset = vaddr & ~ PAGE_MASK ;
buf - > size = size ;
2012-02-16 19:19:08 +04:00
vma = find_vma ( current - > mm , vaddr ) ;
if ( vma & & ( vma - > vm_flags & VM_PFNMAP ) & & ( vma - > vm_pgoff ) ) {
if ( vb2_get_contig_userptr ( vaddr , size , & vma , & physp ) )
goto fail_pages_array_alloc ;
buf - > vma = vma ;
buf - > vaddr = ioremap_nocache ( physp , size ) ;
if ( ! buf - > vaddr )
goto fail_pages_array_alloc ;
} else {
first = vaddr > > PAGE_SHIFT ;
last = ( vaddr + size - 1 ) > > PAGE_SHIFT ;
buf - > n_pages = last - first + 1 ;
buf - > pages = kzalloc ( buf - > n_pages * sizeof ( struct page * ) ,
GFP_KERNEL ) ;
if ( ! buf - > pages )
goto fail_pages_array_alloc ;
/* current->mm->mmap_sem is taken by videobuf2 core */
n_pages = get_user_pages ( current , current - > mm ,
vaddr & PAGE_MASK , buf - > n_pages ,
2014-11-18 15:50:58 +03:00
dma_dir = = DMA_FROM_DEVICE ,
1 , /* force */
2012-02-16 19:19:08 +04:00
buf - > pages , NULL ) ;
if ( n_pages ! = buf - > n_pages )
goto fail_get_user_pages ;
buf - > vaddr = vm_map_ram ( buf - > pages , buf - > n_pages , - 1 ,
PAGE_KERNEL ) ;
if ( ! buf - > vaddr )
goto fail_get_user_pages ;
}
2011-10-13 14:30:51 +04:00
buf - > vaddr + = offset ;
return buf ;
fail_get_user_pages :
pr_debug ( " get_user_pages requested/got: %d/%d] \n " , n_pages ,
buf - > n_pages ) ;
while ( - - n_pages > = 0 )
put_page ( buf - > pages [ n_pages ] ) ;
kfree ( buf - > pages ) ;
fail_pages_array_alloc :
kfree ( buf ) ;
return NULL ;
}
static void vb2_vmalloc_put_userptr ( void * buf_priv )
2010-10-11 17:58:53 +04:00
{
struct vb2_vmalloc_buf * buf = buf_priv ;
2011-10-13 14:30:51 +04:00
unsigned long vaddr = ( unsigned long ) buf - > vaddr & PAGE_MASK ;
unsigned int i ;
2012-02-16 19:19:08 +04:00
if ( buf - > pages ) {
if ( vaddr )
vm_unmap_ram ( ( void * ) vaddr , buf - > n_pages ) ;
for ( i = 0 ; i < buf - > n_pages ; + + i ) {
2014-11-18 15:50:58 +03:00
if ( buf - > dma_dir = = DMA_FROM_DEVICE )
2012-02-16 19:19:08 +04:00
set_page_dirty_lock ( buf - > pages [ i ] ) ;
put_page ( buf - > pages [ i ] ) ;
}
kfree ( buf - > pages ) ;
} else {
if ( buf - > vma )
vb2_put_vma ( buf - > vma ) ;
iounmap ( buf - > vaddr ) ;
2011-10-13 14:30:51 +04:00
}
kfree ( buf ) ;
}
2010-10-11 17:58:53 +04:00
2011-10-13 14:30:51 +04:00
static void * vb2_vmalloc_vaddr ( void * buf_priv )
{
struct vb2_vmalloc_buf * buf = buf_priv ;
2010-10-11 17:58:53 +04:00
if ( ! buf - > vaddr ) {
2011-10-13 14:30:51 +04:00
pr_err ( " Address of an unallocated plane requested "
" or cannot map user pointer \n " ) ;
2010-10-11 17:58:53 +04:00
return NULL ;
}
return buf - > vaddr ;
}
static unsigned int vb2_vmalloc_num_users ( void * buf_priv )
{
struct vb2_vmalloc_buf * buf = buf_priv ;
return atomic_read ( & buf - > refcount ) ;
}
static int vb2_vmalloc_mmap ( void * buf_priv , struct vm_area_struct * vma )
{
struct vb2_vmalloc_buf * buf = buf_priv ;
int ret ;
if ( ! buf ) {
2011-10-13 14:30:51 +04:00
pr_err ( " No memory to map \n " ) ;
2010-10-11 17:58:53 +04:00
return - EINVAL ;
}
ret = remap_vmalloc_range ( vma , buf - > vaddr , 0 ) ;
if ( ret ) {
2011-10-13 14:30:51 +04:00
pr_err ( " Remapping vmalloc memory, error: %d \n " , ret ) ;
2010-10-11 17:58:53 +04:00
return ret ;
}
/*
* Make sure that vm_areas for 2 buffers won ' t be merged together
*/
vma - > vm_flags | = VM_DONTEXPAND ;
/*
* Use common vm_area operations to track buffer refcount .
*/
vma - > vm_private_data = & buf - > handler ;
vma - > vm_ops = & vb2_common_vm_ops ;
vma - > vm_ops - > open ( vma ) ;
return 0 ;
}
2012-06-14 17:37:46 +04:00
/*********************************************/
/* callbacks for DMABUF buffers */
/*********************************************/
static int vb2_vmalloc_map_dmabuf ( void * mem_priv )
{
struct vb2_vmalloc_buf * buf = mem_priv ;
buf - > vaddr = dma_buf_vmap ( buf - > dbuf ) ;
return buf - > vaddr ? 0 : - EFAULT ;
}
static void vb2_vmalloc_unmap_dmabuf ( void * mem_priv )
{
struct vb2_vmalloc_buf * buf = mem_priv ;
dma_buf_vunmap ( buf - > dbuf , buf - > vaddr ) ;
buf - > vaddr = NULL ;
}
static void vb2_vmalloc_detach_dmabuf ( void * mem_priv )
{
struct vb2_vmalloc_buf * buf = mem_priv ;
if ( buf - > vaddr )
dma_buf_vunmap ( buf - > dbuf , buf - > vaddr ) ;
kfree ( buf ) ;
}
static void * vb2_vmalloc_attach_dmabuf ( void * alloc_ctx , struct dma_buf * dbuf ,
2014-11-18 15:50:58 +03:00
unsigned long size , enum dma_data_direction dma_dir )
2012-06-14 17:37:46 +04:00
{
struct vb2_vmalloc_buf * buf ;
if ( dbuf - > size < size )
return ERR_PTR ( - EFAULT ) ;
buf = kzalloc ( sizeof ( * buf ) , GFP_KERNEL ) ;
if ( ! buf )
return ERR_PTR ( - ENOMEM ) ;
buf - > dbuf = dbuf ;
2014-11-18 15:50:58 +03:00
buf - > dma_dir = dma_dir ;
2012-06-14 17:37:46 +04:00
buf - > size = size ;
return buf ;
}
2010-10-11 17:58:53 +04:00
const struct vb2_mem_ops vb2_vmalloc_memops = {
. alloc = vb2_vmalloc_alloc ,
. put = vb2_vmalloc_put ,
2011-10-13 14:30:51 +04:00
. get_userptr = vb2_vmalloc_get_userptr ,
. put_userptr = vb2_vmalloc_put_userptr ,
2012-06-14 17:37:46 +04:00
. map_dmabuf = vb2_vmalloc_map_dmabuf ,
. unmap_dmabuf = vb2_vmalloc_unmap_dmabuf ,
. attach_dmabuf = vb2_vmalloc_attach_dmabuf ,
. detach_dmabuf = vb2_vmalloc_detach_dmabuf ,
2010-10-11 17:58:53 +04:00
. vaddr = vb2_vmalloc_vaddr ,
. mmap = vb2_vmalloc_mmap ,
. num_users = vb2_vmalloc_num_users ,
} ;
EXPORT_SYMBOL_GPL ( vb2_vmalloc_memops ) ;
MODULE_DESCRIPTION ( " vmalloc memory handling routines for videobuf2 " ) ;
2011-03-13 21:23:32 +03:00
MODULE_AUTHOR ( " Pawel Osciak <pawel@osciak.com> " ) ;
2010-10-11 17:58:53 +04:00
MODULE_LICENSE ( " GPL " ) ;