2018-04-30 11:10:58 -07:00
// SPDX-License-Identifier: GPL-2.0+
/* Copyright (C) 2015-2018 Broadcom */
/**
* DOC : V3D GEM BO management support
*
* Compared to VC4 ( V3D 2. x ) , V3D 3.3 introduces an MMU between the
* GPU and the bus , allowing us to use shmem objects for our storage
* instead of CMA .
*
* Physically contiguous objects may still be imported to V3D , but the
* driver doesn ' t allocate physically contiguous objects on its own .
* Display engines requiring physically contiguous allocations should
* look into Mesa ' s " renderonly " support ( as used by the Mesa pl111
* driver ) for an example of how to integrate with V3D .
*
* Long term , we should support evicting pages from the MMU when under
* memory pressure ( thus the v3d_bo_get_pages ( ) refcounting ) , but
* that ' s not a high priority since our systems tend to not have swap .
*/
# include <linux/dma-buf.h>
# include <linux/pfn_t.h>
# include "v3d_drv.h"
# include "uapi/drm/v3d_drm.h"
/* Called DRM core on the last userspace/kernel unreference of the
* BO .
*/
void v3d_free_object ( struct drm_gem_object * obj )
{
struct v3d_dev * v3d = to_v3d_dev ( obj - > dev ) ;
struct v3d_bo * bo = to_v3d_bo ( obj ) ;
2019-03-14 09:34:51 -07:00
v3d_mmu_remove_ptes ( bo ) ;
2018-04-30 11:10:58 -07:00
mutex_lock ( & v3d - > bo_lock ) ;
v3d - > bo_stats . num_allocated - - ;
v3d - > bo_stats . pages_allocated - = obj - > size > > PAGE_SHIFT ;
mutex_unlock ( & v3d - > bo_lock ) ;
spin_lock ( & v3d - > mm_lock ) ;
drm_mm_remove_node ( & bo - > node ) ;
spin_unlock ( & v3d - > mm_lock ) ;
2019-03-14 09:34:51 -07:00
/* GPU execution may have dirtied any pages in the BO. */
bo - > base . pages_mark_dirty_on_put = true ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
drm_gem_shmem_free_object ( obj ) ;
2018-04-30 11:10:58 -07:00
}
2019-03-14 09:34:51 -07:00
static const struct drm_gem_object_funcs v3d_gem_funcs = {
. free = v3d_free_object ,
. print_info = drm_gem_shmem_print_info ,
. pin = drm_gem_shmem_pin ,
. unpin = drm_gem_shmem_unpin ,
. get_sg_table = drm_gem_shmem_get_sg_table ,
. vmap = drm_gem_shmem_vmap ,
. vunmap = drm_gem_shmem_vunmap ,
2019-10-16 13:51:54 +02:00
. mmap = drm_gem_shmem_mmap ,
2019-03-14 09:34:51 -07:00
} ;
/* gem_create_object function for allocating a BO struct and doing
* early setup .
*/
struct drm_gem_object * v3d_create_object ( struct drm_device * dev , size_t size )
2018-04-30 11:10:58 -07:00
{
2019-03-14 09:34:51 -07:00
struct v3d_bo * bo ;
struct drm_gem_object * obj ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
if ( size = = 0 )
return NULL ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
bo = kzalloc ( sizeof ( * bo ) , GFP_KERNEL ) ;
if ( ! bo )
return NULL ;
obj = & bo - > base . base ;
2018-07-04 20:25:57 +05:30
2019-03-14 09:34:51 -07:00
obj - > funcs = & v3d_gem_funcs ;
INIT_LIST_HEAD ( & bo - > unref_head ) ;
return & bo - > base . base ;
2018-04-30 11:10:58 -07:00
}
2019-03-14 09:34:51 -07:00
static int
v3d_bo_create_finish ( struct drm_gem_object * obj )
2018-04-30 11:10:58 -07:00
{
2019-03-14 09:34:51 -07:00
struct v3d_dev * v3d = to_v3d_dev ( obj - > dev ) ;
struct v3d_bo * bo = to_v3d_bo ( obj ) ;
struct sg_table * sgt ;
2018-04-30 11:10:58 -07:00
int ret ;
2019-03-14 09:34:51 -07:00
/* So far we pin the BO in the MMU for its lifetime, so use
* shmem ' s helper for getting a lifetime sgt .
*/
sgt = drm_gem_shmem_get_pages_sgt ( & bo - > base . base ) ;
if ( IS_ERR ( sgt ) )
return PTR_ERR ( sgt ) ;
spin_lock ( & v3d - > mm_lock ) ;
/* Allocate the object's space in the GPU's page tables.
* Inserting PTEs will happen later , but the offset is for the
* lifetime of the BO .
*/
ret = drm_mm_insert_node_generic ( & v3d - > mm , & bo - > node ,
obj - > size > > PAGE_SHIFT ,
GMP_GRANULARITY > > PAGE_SHIFT , 0 , 0 ) ;
spin_unlock ( & v3d - > mm_lock ) ;
2018-04-30 11:10:58 -07:00
if ( ret )
return ret ;
2019-03-14 09:34:51 -07:00
/* Track stats for /debug/dri/n/bo_stats. */
mutex_lock ( & v3d - > bo_lock ) ;
v3d - > bo_stats . num_allocated + + ;
v3d - > bo_stats . pages_allocated + = obj - > size > > PAGE_SHIFT ;
mutex_unlock ( & v3d - > bo_lock ) ;
v3d_mmu_insert_ptes ( bo ) ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
return 0 ;
2018-04-30 11:10:58 -07:00
}
2019-03-14 09:34:51 -07:00
struct v3d_bo * v3d_bo_create ( struct drm_device * dev , struct drm_file * file_priv ,
size_t unaligned_size )
2018-04-30 11:10:58 -07:00
{
2019-03-14 09:34:51 -07:00
struct drm_gem_shmem_object * shmem_obj ;
struct v3d_bo * bo ;
2018-04-30 11:10:58 -07:00
int ret ;
2019-03-14 09:34:51 -07:00
shmem_obj = drm_gem_shmem_create ( dev , unaligned_size ) ;
2019-03-21 09:27:31 +03:00
if ( IS_ERR ( shmem_obj ) )
return ERR_CAST ( shmem_obj ) ;
2019-03-14 09:34:51 -07:00
bo = to_v3d_bo ( & shmem_obj - > base ) ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
ret = v3d_bo_create_finish ( & shmem_obj - > base ) ;
if ( ret )
goto free_obj ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
return bo ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
free_obj :
drm_gem_shmem_free_object ( & shmem_obj - > base ) ;
return ERR_PTR ( ret ) ;
2018-04-30 11:10:58 -07:00
}
struct drm_gem_object *
v3d_prime_import_sg_table ( struct drm_device * dev ,
struct dma_buf_attachment * attach ,
struct sg_table * sgt )
{
struct drm_gem_object * obj ;
2019-03-14 09:34:51 -07:00
int ret ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
obj = drm_gem_shmem_prime_import_sg_table ( dev , attach , sgt ) ;
if ( IS_ERR ( obj ) )
return obj ;
2019-02-07 15:26:13 -08:00
2019-03-14 09:34:51 -07:00
ret = v3d_bo_create_finish ( obj ) ;
if ( ret ) {
drm_gem_shmem_free_object ( obj ) ;
return ERR_PTR ( ret ) ;
}
2018-04-30 11:10:58 -07:00
return obj ;
}
int v3d_create_bo_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_v3d_create_bo * args = data ;
struct v3d_bo * bo = NULL ;
int ret ;
if ( args - > flags ! = 0 ) {
DRM_INFO ( " unknown create_bo flags: %d \n " , args - > flags ) ;
return - EINVAL ;
}
bo = v3d_bo_create ( dev , file_priv , PAGE_ALIGN ( args - > size ) ) ;
if ( IS_ERR ( bo ) )
return PTR_ERR ( bo ) ;
args - > offset = bo - > node . start < < PAGE_SHIFT ;
2019-03-14 09:34:51 -07:00
ret = drm_gem_handle_create ( file_priv , & bo - > base . base , & args - > handle ) ;
2020-05-15 10:51:12 +01:00
drm_gem_object_put ( & bo - > base . base ) ;
2018-04-30 11:10:58 -07:00
return ret ;
}
int v3d_mmap_bo_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_v3d_mmap_bo * args = data ;
struct drm_gem_object * gem_obj ;
if ( args - > flags ! = 0 ) {
DRM_INFO ( " unknown mmap_bo flags: %d \n " , args - > flags ) ;
return - EINVAL ;
}
gem_obj = drm_gem_object_lookup ( file_priv , args - > handle ) ;
if ( ! gem_obj ) {
DRM_DEBUG ( " Failed to look up GEM BO %d \n " , args - > handle ) ;
return - ENOENT ;
}
2019-03-14 09:34:51 -07:00
args - > offset = drm_vma_node_offset_addr ( & gem_obj - > vma_node ) ;
2020-05-15 10:51:12 +01:00
drm_gem_object_put ( gem_obj ) ;
2018-04-30 11:10:58 -07:00
2019-03-14 09:34:51 -07:00
return 0 ;
2018-04-30 11:10:58 -07:00
}
int v3d_get_bo_offset_ioctl ( struct drm_device * dev , void * data ,
struct drm_file * file_priv )
{
struct drm_v3d_get_bo_offset * args = data ;
struct drm_gem_object * gem_obj ;
struct v3d_bo * bo ;
gem_obj = drm_gem_object_lookup ( file_priv , args - > handle ) ;
if ( ! gem_obj ) {
DRM_DEBUG ( " Failed to look up GEM BO %d \n " , args - > handle ) ;
return - ENOENT ;
}
bo = to_v3d_bo ( gem_obj ) ;
args - > offset = bo - > node . start < < PAGE_SHIFT ;
2020-05-15 10:51:12 +01:00
drm_gem_object_put ( gem_obj ) ;
2018-04-30 11:10:58 -07:00
return 0 ;
}