1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright (C) 2015-2018 Broadcom */
5 * DOC: V3D GEM BO management support
7 * Compared to VC4 (V3D 2.x), V3D 3.3 introduces an MMU between the
8 * GPU and the bus, allowing us to use shmem objects for our storage
11 * Physically contiguous objects may still be imported to V3D, but the
12 * driver doesn't allocate physically contiguous objects on its own.
13 * Display engines requiring physically contiguous allocations should
14 * look into Mesa's "renderonly" support (as used by the Mesa pl111
15 * driver) for an example of how to integrate with V3D.
17 * Long term, we should support evicting pages from the MMU when under
18 * memory pressure (thus the v3d_bo_get_pages() refcounting), but
19 * that's not a high priority since our systems tend to not have swap.
22 #include <linux/dma-buf.h>
23 #include <linux/pfn_t.h>
26 #include "uapi/drm/v3d_drm.h"
28 /* Called DRM core on the last userspace/kernel unreference of the
31 void v3d_free_object(struct drm_gem_object
*obj
)
33 struct v3d_dev
*v3d
= to_v3d_dev(obj
->dev
);
34 struct v3d_bo
*bo
= to_v3d_bo(obj
);
36 v3d_mmu_remove_ptes(bo
);
38 mutex_lock(&v3d
->bo_lock
);
39 v3d
->bo_stats
.num_allocated
--;
40 v3d
->bo_stats
.pages_allocated
-= obj
->size
>> PAGE_SHIFT
;
41 mutex_unlock(&v3d
->bo_lock
);
43 spin_lock(&v3d
->mm_lock
);
44 drm_mm_remove_node(&bo
->node
);
45 spin_unlock(&v3d
->mm_lock
);
47 /* GPU execution may have dirtied any pages in the BO. */
48 bo
->base
.pages_mark_dirty_on_put
= true;
50 drm_gem_shmem_free_object(obj
);
53 static const struct drm_gem_object_funcs v3d_gem_funcs
= {
54 .free
= v3d_free_object
,
55 .print_info
= drm_gem_shmem_print_info
,
56 .pin
= drm_gem_shmem_pin
,
57 .unpin
= drm_gem_shmem_unpin
,
58 .get_sg_table
= drm_gem_shmem_get_sg_table
,
59 .vmap
= drm_gem_shmem_vmap
,
60 .vunmap
= drm_gem_shmem_vunmap
,
61 .mmap
= drm_gem_shmem_mmap
,
64 /* gem_create_object function for allocating a BO struct and doing
67 struct drm_gem_object
*v3d_create_object(struct drm_device
*dev
, size_t size
)
70 struct drm_gem_object
*obj
;
75 bo
= kzalloc(sizeof(*bo
), GFP_KERNEL
);
80 obj
->funcs
= &v3d_gem_funcs
;
81 bo
->base
.map_wc
= true;
82 INIT_LIST_HEAD(&bo
->unref_head
);
84 return &bo
->base
.base
;
88 v3d_bo_create_finish(struct drm_gem_object
*obj
)
90 struct v3d_dev
*v3d
= to_v3d_dev(obj
->dev
);
91 struct v3d_bo
*bo
= to_v3d_bo(obj
);
95 /* So far we pin the BO in the MMU for its lifetime, so use
96 * shmem's helper for getting a lifetime sgt.
98 sgt
= drm_gem_shmem_get_pages_sgt(&bo
->base
.base
);
102 spin_lock(&v3d
->mm_lock
);
103 /* Allocate the object's space in the GPU's page tables.
104 * Inserting PTEs will happen later, but the offset is for the
105 * lifetime of the BO.
107 ret
= drm_mm_insert_node_generic(&v3d
->mm
, &bo
->node
,
108 obj
->size
>> PAGE_SHIFT
,
109 GMP_GRANULARITY
>> PAGE_SHIFT
, 0, 0);
110 spin_unlock(&v3d
->mm_lock
);
114 /* Track stats for /debug/dri/n/bo_stats. */
115 mutex_lock(&v3d
->bo_lock
);
116 v3d
->bo_stats
.num_allocated
++;
117 v3d
->bo_stats
.pages_allocated
+= obj
->size
>> PAGE_SHIFT
;
118 mutex_unlock(&v3d
->bo_lock
);
120 v3d_mmu_insert_ptes(bo
);
125 struct v3d_bo
*v3d_bo_create(struct drm_device
*dev
, struct drm_file
*file_priv
,
126 size_t unaligned_size
)
128 struct drm_gem_shmem_object
*shmem_obj
;
132 shmem_obj
= drm_gem_shmem_create(dev
, unaligned_size
);
133 if (IS_ERR(shmem_obj
))
134 return ERR_CAST(shmem_obj
);
135 bo
= to_v3d_bo(&shmem_obj
->base
);
137 ret
= v3d_bo_create_finish(&shmem_obj
->base
);
144 drm_gem_shmem_free_object(&shmem_obj
->base
);
148 struct drm_gem_object
*
149 v3d_prime_import_sg_table(struct drm_device
*dev
,
150 struct dma_buf_attachment
*attach
,
151 struct sg_table
*sgt
)
153 struct drm_gem_object
*obj
;
156 obj
= drm_gem_shmem_prime_import_sg_table(dev
, attach
, sgt
);
160 ret
= v3d_bo_create_finish(obj
);
162 drm_gem_shmem_free_object(obj
);
169 int v3d_create_bo_ioctl(struct drm_device
*dev
, void *data
,
170 struct drm_file
*file_priv
)
172 struct drm_v3d_create_bo
*args
= data
;
173 struct v3d_bo
*bo
= NULL
;
176 if (args
->flags
!= 0) {
177 DRM_INFO("unknown create_bo flags: %d\n", args
->flags
);
181 bo
= v3d_bo_create(dev
, file_priv
, PAGE_ALIGN(args
->size
));
185 args
->offset
= bo
->node
.start
<< PAGE_SHIFT
;
187 ret
= drm_gem_handle_create(file_priv
, &bo
->base
.base
, &args
->handle
);
188 drm_gem_object_put(&bo
->base
.base
);
193 int v3d_mmap_bo_ioctl(struct drm_device
*dev
, void *data
,
194 struct drm_file
*file_priv
)
196 struct drm_v3d_mmap_bo
*args
= data
;
197 struct drm_gem_object
*gem_obj
;
199 if (args
->flags
!= 0) {
200 DRM_INFO("unknown mmap_bo flags: %d\n", args
->flags
);
204 gem_obj
= drm_gem_object_lookup(file_priv
, args
->handle
);
206 DRM_DEBUG("Failed to look up GEM BO %d\n", args
->handle
);
210 args
->offset
= drm_vma_node_offset_addr(&gem_obj
->vma_node
);
211 drm_gem_object_put(gem_obj
);
216 int v3d_get_bo_offset_ioctl(struct drm_device
*dev
, void *data
,
217 struct drm_file
*file_priv
)
219 struct drm_v3d_get_bo_offset
*args
= data
;
220 struct drm_gem_object
*gem_obj
;
223 gem_obj
= drm_gem_object_lookup(file_priv
, args
->handle
);
225 DRM_DEBUG("Failed to look up GEM BO %d\n", args
->handle
);
228 bo
= to_v3d_bo(gem_obj
);
230 args
->offset
= bo
->node
.start
<< PAGE_SHIFT
;
232 drm_gem_object_put(gem_obj
);