2014-08-22 18:36:26 +08:00
/*
* Copyright ( C ) Fuzhou Rockchip Electronics Co . Ltd
* Author : Mark Yao < mark . yao @ rock - chips . com >
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <drm/drm.h>
# include <drm/drmP.h>
# include <drm/drm_gem.h>
# include <drm/drm_vma_manager.h>
# include <linux/dma-attrs.h>
# include "rockchip_drm_drv.h"
# include "rockchip_drm_gem.h"
static int rockchip_gem_alloc_buf ( struct rockchip_gem_object * rk_obj )
{
struct drm_gem_object * obj = & rk_obj - > base ;
struct drm_device * drm = obj - > dev ;
init_dma_attrs ( & rk_obj - > dma_attrs ) ;
dma_set_attr ( DMA_ATTR_WRITE_COMBINE , & rk_obj - > dma_attrs ) ;
/* TODO(djkurtz): Use DMA_ATTR_NO_KERNEL_MAPPING except for fbdev */
rk_obj - > kvaddr = dma_alloc_attrs ( drm - > dev , obj - > size ,
& rk_obj - > dma_addr , GFP_KERNEL ,
& rk_obj - > dma_attrs ) ;
2015-01-07 17:27:06 +08:00
if ( ! rk_obj - > kvaddr ) {
DRM_ERROR ( " failed to allocate %#x byte dma buffer " , obj - > size ) ;
return - ENOMEM ;
2014-08-22 18:36:26 +08:00
}
return 0 ;
}
static void rockchip_gem_free_buf ( struct rockchip_gem_object * rk_obj )
{
struct drm_gem_object * obj = & rk_obj - > base ;
struct drm_device * drm = obj - > dev ;
dma_free_attrs ( drm - > dev , obj - > size , rk_obj - > kvaddr , rk_obj - > dma_addr ,
& rk_obj - > dma_attrs ) ;
}
int rockchip_gem_mmap_buf ( struct drm_gem_object * obj ,
struct vm_area_struct * vma )
{
struct rockchip_gem_object * rk_obj = to_rockchip_obj ( obj ) ;
struct drm_device * drm = obj - > dev ;
unsigned long vm_size ;
vma - > vm_flags | = VM_IO | VM_DONTEXPAND | VM_DONTDUMP ;
vm_size = vma - > vm_end - vma - > vm_start ;
if ( vm_size > obj - > size )
return - EINVAL ;
return dma_mmap_attrs ( drm - > dev , vma , rk_obj - > kvaddr , rk_obj - > dma_addr ,
obj - > size , & rk_obj - > dma_attrs ) ;
}
/* drm driver mmap file operations */
int rockchip_gem_mmap ( struct file * filp , struct vm_area_struct * vma )
{
struct drm_file * priv = filp - > private_data ;
struct drm_device * dev = priv - > minor - > dev ;
struct drm_gem_object * obj ;
struct drm_vma_offset_node * node ;
int ret ;
if ( drm_device_is_unplugged ( dev ) )
return - ENODEV ;
mutex_lock ( & dev - > struct_mutex ) ;
node = drm_vma_offset_exact_lookup ( dev - > vma_offset_manager ,
vma - > vm_pgoff ,
vma_pages ( vma ) ) ;
if ( ! node ) {
mutex_unlock ( & dev - > struct_mutex ) ;
DRM_ERROR ( " failed to find vma node. \n " ) ;
return - EINVAL ;
} else if ( ! drm_vma_node_is_allowed ( node , filp ) ) {
mutex_unlock ( & dev - > struct_mutex ) ;
return - EACCES ;
}
obj = container_of ( node , struct drm_gem_object , vma_node ) ;
ret = rockchip_gem_mmap_buf ( obj , vma ) ;
mutex_unlock ( & dev - > struct_mutex ) ;
return ret ;
}
struct rockchip_gem_object *
rockchip_gem_create_object ( struct drm_device * drm , unsigned int size )
{
struct rockchip_gem_object * rk_obj ;
struct drm_gem_object * obj ;
int ret ;
size = round_up ( size , PAGE_SIZE ) ;
rk_obj = kzalloc ( sizeof ( * rk_obj ) , GFP_KERNEL ) ;
if ( ! rk_obj )
return ERR_PTR ( - ENOMEM ) ;
obj = & rk_obj - > base ;
drm_gem_private_object_init ( drm , obj , size ) ;
ret = rockchip_gem_alloc_buf ( rk_obj ) ;
if ( ret )
goto err_free_rk_obj ;
return rk_obj ;
err_free_rk_obj :
kfree ( rk_obj ) ;
return ERR_PTR ( ret ) ;
}
/*
* rockchip_gem_free_object - ( struct drm_driver ) - > gem_free_object callback
* function
*/
void rockchip_gem_free_object ( struct drm_gem_object * obj )
{
struct rockchip_gem_object * rk_obj ;
drm_gem_free_mmap_offset ( obj ) ;
rk_obj = to_rockchip_obj ( obj ) ;
rockchip_gem_free_buf ( rk_obj ) ;
kfree ( rk_obj ) ;
}
/*
* rockchip_gem_create_with_handle - allocate an object with the given
* size and create a gem handle on it
*
* returns a struct rockchip_gem_object * on success or ERR_PTR values
* on failure .
*/
static struct rockchip_gem_object *
rockchip_gem_create_with_handle ( struct drm_file * file_priv ,
struct drm_device * drm , unsigned int size ,
unsigned int * handle )
{
struct rockchip_gem_object * rk_obj ;
struct drm_gem_object * obj ;
int ret ;
rk_obj = rockchip_gem_create_object ( drm , size ) ;
if ( IS_ERR ( rk_obj ) )
return ERR_CAST ( rk_obj ) ;
obj = & rk_obj - > base ;
/*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see .
*/
ret = drm_gem_handle_create ( file_priv , obj , handle ) ;
if ( ret )
goto err_handle_create ;
/* drop reference from allocate - handle holds it now. */
drm_gem_object_unreference_unlocked ( obj ) ;
return rk_obj ;
err_handle_create :
rockchip_gem_free_object ( obj ) ;
return ERR_PTR ( ret ) ;
}
int rockchip_gem_dumb_map_offset ( struct drm_file * file_priv ,
struct drm_device * dev , uint32_t handle ,
uint64_t * offset )
{
struct drm_gem_object * obj ;
int ret ;
mutex_lock ( & dev - > struct_mutex ) ;
obj = drm_gem_object_lookup ( dev , file_priv , handle ) ;
if ( ! obj ) {
DRM_ERROR ( " failed to lookup gem object. \n " ) ;
ret = - EINVAL ;
goto unlock ;
}
ret = drm_gem_create_mmap_offset ( obj ) ;
if ( ret )
goto out ;
* offset = drm_vma_node_offset_addr ( & obj - > vma_node ) ;
DRM_DEBUG_KMS ( " offset = 0x%llx \n " , * offset ) ;
out :
drm_gem_object_unreference ( obj ) ;
unlock :
mutex_unlock ( & dev - > struct_mutex ) ;
return ret ;
}
/*
* rockchip_gem_dumb_create - ( struct drm_driver ) - > dumb_create callback
* function
*
* This aligns the pitch and size arguments to the minimum required . wrap
* this into your own function if you need bigger alignment .
*/
int rockchip_gem_dumb_create ( struct drm_file * file_priv ,
struct drm_device * dev ,
struct drm_mode_create_dumb * args )
{
struct rockchip_gem_object * rk_obj ;
int min_pitch = DIV_ROUND_UP ( args - > width * args - > bpp , 8 ) ;
/*
* align to 64 bytes since Mali requires it .
*/
min_pitch = ALIGN ( min_pitch , 64 ) ;
if ( args - > pitch < min_pitch )
args - > pitch = min_pitch ;
if ( args - > size < args - > pitch * args - > height )
args - > size = args - > pitch * args - > height ;
rk_obj = rockchip_gem_create_with_handle ( file_priv , dev , args - > size ,
& args - > handle ) ;
return PTR_ERR_OR_ZERO ( rk_obj ) ;
}
/*
* Allocate a sg_table for this GEM object .
* Note : Both the table ' s contents , and the sg_table itself must be freed by
* the caller .
* Returns a pointer to the newly allocated sg_table , or an ERR_PTR ( ) error .
*/
struct sg_table * rockchip_gem_prime_get_sg_table ( struct drm_gem_object * obj )
{
struct rockchip_gem_object * rk_obj = to_rockchip_obj ( obj ) ;
struct drm_device * drm = obj - > dev ;
struct sg_table * sgt ;
int ret ;
sgt = kzalloc ( sizeof ( * sgt ) , GFP_KERNEL ) ;
if ( ! sgt )
return ERR_PTR ( - ENOMEM ) ;
ret = dma_get_sgtable_attrs ( drm - > dev , sgt , rk_obj - > kvaddr ,
rk_obj - > dma_addr , obj - > size ,
& rk_obj - > dma_attrs ) ;
if ( ret ) {
DRM_ERROR ( " failed to allocate sgt, %d \n " , ret ) ;
kfree ( sgt ) ;
return ERR_PTR ( ret ) ;
}
return sgt ;
}
void * rockchip_gem_prime_vmap ( struct drm_gem_object * obj )
{
struct rockchip_gem_object * rk_obj = to_rockchip_obj ( obj ) ;
return rk_obj - > kvaddr ;
}
void rockchip_gem_prime_vunmap ( struct drm_gem_object * obj , void * vaddr )
{
/* Nothing to do */
}