1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0+
2*4882a593Smuzhiyun /* Copyright (C) 2015-2018 Broadcom */
3*4882a593Smuzhiyun
4*4882a593Smuzhiyun /**
5*4882a593Smuzhiyun * DOC: V3D GEM BO management support
6*4882a593Smuzhiyun *
7*4882a593Smuzhiyun * Compared to VC4 (V3D 2.x), V3D 3.3 introduces an MMU between the
8*4882a593Smuzhiyun * GPU and the bus, allowing us to use shmem objects for our storage
9*4882a593Smuzhiyun * instead of CMA.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * Physically contiguous objects may still be imported to V3D, but the
12*4882a593Smuzhiyun * driver doesn't allocate physically contiguous objects on its own.
13*4882a593Smuzhiyun * Display engines requiring physically contiguous allocations should
14*4882a593Smuzhiyun * look into Mesa's "renderonly" support (as used by the Mesa pl111
15*4882a593Smuzhiyun * driver) for an example of how to integrate with V3D.
16*4882a593Smuzhiyun *
17*4882a593Smuzhiyun * Long term, we should support evicting pages from the MMU when under
18*4882a593Smuzhiyun * memory pressure (thus the v3d_bo_get_pages() refcounting), but
19*4882a593Smuzhiyun * that's not a high priority since our systems tend to not have swap.
20*4882a593Smuzhiyun */
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #include <linux/dma-buf.h>
23*4882a593Smuzhiyun #include <linux/pfn_t.h>
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #include "v3d_drv.h"
26*4882a593Smuzhiyun #include "uapi/drm/v3d_drm.h"
27*4882a593Smuzhiyun
28*4882a593Smuzhiyun /* Called DRM core on the last userspace/kernel unreference of the
29*4882a593Smuzhiyun * BO.
30*4882a593Smuzhiyun */
v3d_free_object(struct drm_gem_object * obj)31*4882a593Smuzhiyun void v3d_free_object(struct drm_gem_object *obj)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun struct v3d_dev *v3d = to_v3d_dev(obj->dev);
34*4882a593Smuzhiyun struct v3d_bo *bo = to_v3d_bo(obj);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun v3d_mmu_remove_ptes(bo);
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun mutex_lock(&v3d->bo_lock);
39*4882a593Smuzhiyun v3d->bo_stats.num_allocated--;
40*4882a593Smuzhiyun v3d->bo_stats.pages_allocated -= obj->size >> PAGE_SHIFT;
41*4882a593Smuzhiyun mutex_unlock(&v3d->bo_lock);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun spin_lock(&v3d->mm_lock);
44*4882a593Smuzhiyun drm_mm_remove_node(&bo->node);
45*4882a593Smuzhiyun spin_unlock(&v3d->mm_lock);
46*4882a593Smuzhiyun
47*4882a593Smuzhiyun /* GPU execution may have dirtied any pages in the BO. */
48*4882a593Smuzhiyun bo->base.pages_mark_dirty_on_put = true;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun drm_gem_shmem_free_object(obj);
51*4882a593Smuzhiyun }
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun static const struct drm_gem_object_funcs v3d_gem_funcs = {
54*4882a593Smuzhiyun .free = v3d_free_object,
55*4882a593Smuzhiyun .print_info = drm_gem_shmem_print_info,
56*4882a593Smuzhiyun .pin = drm_gem_shmem_pin,
57*4882a593Smuzhiyun .unpin = drm_gem_shmem_unpin,
58*4882a593Smuzhiyun .get_sg_table = drm_gem_shmem_get_sg_table,
59*4882a593Smuzhiyun .vmap = drm_gem_shmem_vmap,
60*4882a593Smuzhiyun .vunmap = drm_gem_shmem_vunmap,
61*4882a593Smuzhiyun .mmap = drm_gem_shmem_mmap,
62*4882a593Smuzhiyun };
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun /* gem_create_object function for allocating a BO struct and doing
65*4882a593Smuzhiyun * early setup.
66*4882a593Smuzhiyun */
v3d_create_object(struct drm_device * dev,size_t size)67*4882a593Smuzhiyun struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun struct v3d_bo *bo;
70*4882a593Smuzhiyun struct drm_gem_object *obj;
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun if (size == 0)
73*4882a593Smuzhiyun return NULL;
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun bo = kzalloc(sizeof(*bo), GFP_KERNEL);
76*4882a593Smuzhiyun if (!bo)
77*4882a593Smuzhiyun return NULL;
78*4882a593Smuzhiyun obj = &bo->base.base;
79*4882a593Smuzhiyun
80*4882a593Smuzhiyun obj->funcs = &v3d_gem_funcs;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun INIT_LIST_HEAD(&bo->unref_head);
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun return &bo->base.base;
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun static int
v3d_bo_create_finish(struct drm_gem_object * obj)88*4882a593Smuzhiyun v3d_bo_create_finish(struct drm_gem_object *obj)
89*4882a593Smuzhiyun {
90*4882a593Smuzhiyun struct v3d_dev *v3d = to_v3d_dev(obj->dev);
91*4882a593Smuzhiyun struct v3d_bo *bo = to_v3d_bo(obj);
92*4882a593Smuzhiyun struct sg_table *sgt;
93*4882a593Smuzhiyun int ret;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun /* So far we pin the BO in the MMU for its lifetime, so use
96*4882a593Smuzhiyun * shmem's helper for getting a lifetime sgt.
97*4882a593Smuzhiyun */
98*4882a593Smuzhiyun sgt = drm_gem_shmem_get_pages_sgt(&bo->base.base);
99*4882a593Smuzhiyun if (IS_ERR(sgt))
100*4882a593Smuzhiyun return PTR_ERR(sgt);
101*4882a593Smuzhiyun
102*4882a593Smuzhiyun spin_lock(&v3d->mm_lock);
103*4882a593Smuzhiyun /* Allocate the object's space in the GPU's page tables.
104*4882a593Smuzhiyun * Inserting PTEs will happen later, but the offset is for the
105*4882a593Smuzhiyun * lifetime of the BO.
106*4882a593Smuzhiyun */
107*4882a593Smuzhiyun ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node,
108*4882a593Smuzhiyun obj->size >> PAGE_SHIFT,
109*4882a593Smuzhiyun GMP_GRANULARITY >> PAGE_SHIFT, 0, 0);
110*4882a593Smuzhiyun spin_unlock(&v3d->mm_lock);
111*4882a593Smuzhiyun if (ret)
112*4882a593Smuzhiyun return ret;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun /* Track stats for /debug/dri/n/bo_stats. */
115*4882a593Smuzhiyun mutex_lock(&v3d->bo_lock);
116*4882a593Smuzhiyun v3d->bo_stats.num_allocated++;
117*4882a593Smuzhiyun v3d->bo_stats.pages_allocated += obj->size >> PAGE_SHIFT;
118*4882a593Smuzhiyun mutex_unlock(&v3d->bo_lock);
119*4882a593Smuzhiyun
120*4882a593Smuzhiyun v3d_mmu_insert_ptes(bo);
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun return 0;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
v3d_bo_create(struct drm_device * dev,struct drm_file * file_priv,size_t unaligned_size)125*4882a593Smuzhiyun struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
126*4882a593Smuzhiyun size_t unaligned_size)
127*4882a593Smuzhiyun {
128*4882a593Smuzhiyun struct drm_gem_shmem_object *shmem_obj;
129*4882a593Smuzhiyun struct v3d_bo *bo;
130*4882a593Smuzhiyun int ret;
131*4882a593Smuzhiyun
132*4882a593Smuzhiyun shmem_obj = drm_gem_shmem_create(dev, unaligned_size);
133*4882a593Smuzhiyun if (IS_ERR(shmem_obj))
134*4882a593Smuzhiyun return ERR_CAST(shmem_obj);
135*4882a593Smuzhiyun bo = to_v3d_bo(&shmem_obj->base);
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun ret = v3d_bo_create_finish(&shmem_obj->base);
138*4882a593Smuzhiyun if (ret)
139*4882a593Smuzhiyun goto free_obj;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun return bo;
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun free_obj:
144*4882a593Smuzhiyun drm_gem_shmem_free_object(&shmem_obj->base);
145*4882a593Smuzhiyun return ERR_PTR(ret);
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun struct drm_gem_object *
v3d_prime_import_sg_table(struct drm_device * dev,struct dma_buf_attachment * attach,struct sg_table * sgt)149*4882a593Smuzhiyun v3d_prime_import_sg_table(struct drm_device *dev,
150*4882a593Smuzhiyun struct dma_buf_attachment *attach,
151*4882a593Smuzhiyun struct sg_table *sgt)
152*4882a593Smuzhiyun {
153*4882a593Smuzhiyun struct drm_gem_object *obj;
154*4882a593Smuzhiyun int ret;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
157*4882a593Smuzhiyun if (IS_ERR(obj))
158*4882a593Smuzhiyun return obj;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun ret = v3d_bo_create_finish(obj);
161*4882a593Smuzhiyun if (ret) {
162*4882a593Smuzhiyun drm_gem_shmem_free_object(obj);
163*4882a593Smuzhiyun return ERR_PTR(ret);
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun
166*4882a593Smuzhiyun return obj;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
v3d_create_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)169*4882a593Smuzhiyun int v3d_create_bo_ioctl(struct drm_device *dev, void *data,
170*4882a593Smuzhiyun struct drm_file *file_priv)
171*4882a593Smuzhiyun {
172*4882a593Smuzhiyun struct drm_v3d_create_bo *args = data;
173*4882a593Smuzhiyun struct v3d_bo *bo = NULL;
174*4882a593Smuzhiyun int ret;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun if (args->flags != 0) {
177*4882a593Smuzhiyun DRM_INFO("unknown create_bo flags: %d\n", args->flags);
178*4882a593Smuzhiyun return -EINVAL;
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun bo = v3d_bo_create(dev, file_priv, PAGE_ALIGN(args->size));
182*4882a593Smuzhiyun if (IS_ERR(bo))
183*4882a593Smuzhiyun return PTR_ERR(bo);
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun args->offset = bo->node.start << PAGE_SHIFT;
186*4882a593Smuzhiyun
187*4882a593Smuzhiyun ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
188*4882a593Smuzhiyun drm_gem_object_put(&bo->base.base);
189*4882a593Smuzhiyun
190*4882a593Smuzhiyun return ret;
191*4882a593Smuzhiyun }
192*4882a593Smuzhiyun
v3d_mmap_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)193*4882a593Smuzhiyun int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
194*4882a593Smuzhiyun struct drm_file *file_priv)
195*4882a593Smuzhiyun {
196*4882a593Smuzhiyun struct drm_v3d_mmap_bo *args = data;
197*4882a593Smuzhiyun struct drm_gem_object *gem_obj;
198*4882a593Smuzhiyun
199*4882a593Smuzhiyun if (args->flags != 0) {
200*4882a593Smuzhiyun DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);
201*4882a593Smuzhiyun return -EINVAL;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun gem_obj = drm_gem_object_lookup(file_priv, args->handle);
205*4882a593Smuzhiyun if (!gem_obj) {
206*4882a593Smuzhiyun DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
207*4882a593Smuzhiyun return -ENOENT;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
211*4882a593Smuzhiyun drm_gem_object_put(gem_obj);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun return 0;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun
v3d_get_bo_offset_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)216*4882a593Smuzhiyun int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
217*4882a593Smuzhiyun struct drm_file *file_priv)
218*4882a593Smuzhiyun {
219*4882a593Smuzhiyun struct drm_v3d_get_bo_offset *args = data;
220*4882a593Smuzhiyun struct drm_gem_object *gem_obj;
221*4882a593Smuzhiyun struct v3d_bo *bo;
222*4882a593Smuzhiyun
223*4882a593Smuzhiyun gem_obj = drm_gem_object_lookup(file_priv, args->handle);
224*4882a593Smuzhiyun if (!gem_obj) {
225*4882a593Smuzhiyun DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
226*4882a593Smuzhiyun return -ENOENT;
227*4882a593Smuzhiyun }
228*4882a593Smuzhiyun bo = to_v3d_bo(gem_obj);
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun args->offset = bo->node.start << PAGE_SHIFT;
231*4882a593Smuzhiyun
232*4882a593Smuzhiyun drm_gem_object_put(gem_obj);
233*4882a593Smuzhiyun return 0;
234*4882a593Smuzhiyun }
235