157692c94SEric Anholt // SPDX-License-Identifier: GPL-2.0+
257692c94SEric Anholt /* Copyright (C) 2015-2018 Broadcom */
357692c94SEric Anholt
457692c94SEric Anholt /**
557692c94SEric Anholt * DOC: V3D GEM BO management support
657692c94SEric Anholt *
757692c94SEric Anholt * Compared to VC4 (V3D 2.x), V3D 3.3 introduces an MMU between the
857692c94SEric Anholt * GPU and the bus, allowing us to use shmem objects for our storage
957692c94SEric Anholt * instead of CMA.
1057692c94SEric Anholt *
1157692c94SEric Anholt * Physically contiguous objects may still be imported to V3D, but the
1257692c94SEric Anholt * driver doesn't allocate physically contiguous objects on its own.
1357692c94SEric Anholt * Display engines requiring physically contiguous allocations should
1457692c94SEric Anholt * look into Mesa's "renderonly" support (as used by the Mesa pl111
1557692c94SEric Anholt * driver) for an example of how to integrate with V3D.
1657692c94SEric Anholt *
1757692c94SEric Anholt * Long term, we should support evicting pages from the MMU when under
1857692c94SEric Anholt * memory pressure (thus the v3d_bo_get_pages() refcounting), but
1957692c94SEric Anholt * that's not a high priority since our systems tend to not have swap.
2057692c94SEric Anholt */
2157692c94SEric Anholt
2257692c94SEric Anholt #include <linux/dma-buf.h>
2357692c94SEric Anholt #include <linux/pfn_t.h>
2457692c94SEric Anholt
2557692c94SEric Anholt #include "v3d_drv.h"
2657692c94SEric Anholt #include "uapi/drm/v3d_drm.h"
2757692c94SEric Anholt
2857692c94SEric Anholt /* Called DRM core on the last userspace/kernel unreference of the
2957692c94SEric Anholt * BO.
3057692c94SEric Anholt */
v3d_free_object(struct drm_gem_object * obj)3157692c94SEric Anholt void v3d_free_object(struct drm_gem_object *obj)
3257692c94SEric Anholt {
3357692c94SEric Anholt struct v3d_dev *v3d = to_v3d_dev(obj->dev);
3457692c94SEric Anholt struct v3d_bo *bo = to_v3d_bo(obj);
3557692c94SEric Anholt
3640609d48SEric Anholt v3d_mmu_remove_ptes(bo);
3740609d48SEric Anholt
3857692c94SEric Anholt mutex_lock(&v3d->bo_lock);
3957692c94SEric Anholt v3d->bo_stats.num_allocated--;
4057692c94SEric Anholt v3d->bo_stats.pages_allocated -= obj->size >> PAGE_SHIFT;
4157692c94SEric Anholt mutex_unlock(&v3d->bo_lock);
4257692c94SEric Anholt
4357692c94SEric Anholt spin_lock(&v3d->mm_lock);
4457692c94SEric Anholt drm_mm_remove_node(&bo->node);
4557692c94SEric Anholt spin_unlock(&v3d->mm_lock);
4657692c94SEric Anholt
4740609d48SEric Anholt /* GPU execution may have dirtied any pages in the BO. */
4840609d48SEric Anholt bo->base.pages_mark_dirty_on_put = true;
4957692c94SEric Anholt
50a193f3b4SThomas Zimmermann drm_gem_shmem_free(&bo->base);
5157692c94SEric Anholt }
5257692c94SEric Anholt
5340609d48SEric Anholt static const struct drm_gem_object_funcs v3d_gem_funcs = {
5440609d48SEric Anholt .free = v3d_free_object,
55c7fbcb71SThomas Zimmermann .print_info = drm_gem_shmem_object_print_info,
56c7fbcb71SThomas Zimmermann .pin = drm_gem_shmem_object_pin,
57c7fbcb71SThomas Zimmermann .unpin = drm_gem_shmem_object_unpin,
58c7fbcb71SThomas Zimmermann .get_sg_table = drm_gem_shmem_object_get_sg_table,
59c7fbcb71SThomas Zimmermann .vmap = drm_gem_shmem_object_vmap,
60c7fbcb71SThomas Zimmermann .vunmap = drm_gem_shmem_object_vunmap,
61c7fbcb71SThomas Zimmermann .mmap = drm_gem_shmem_object_mmap,
62*d315bdbfSThomas Zimmermann .vm_ops = &drm_gem_shmem_vm_ops,
6340609d48SEric Anholt };
6440609d48SEric Anholt
6540609d48SEric Anholt /* gem_create_object function for allocating a BO struct and doing
6640609d48SEric Anholt * early setup.
6740609d48SEric Anholt */
v3d_create_object(struct drm_device * dev,size_t size)6840609d48SEric Anholt struct drm_gem_object *v3d_create_object(struct drm_device *dev, size_t size)
6957692c94SEric Anholt {
7040609d48SEric Anholt struct v3d_bo *bo;
7140609d48SEric Anholt struct drm_gem_object *obj;
7240609d48SEric Anholt
7340609d48SEric Anholt if (size == 0)
744ff22f48SThomas Zimmermann return ERR_PTR(-EINVAL);
7540609d48SEric Anholt
7640609d48SEric Anholt bo = kzalloc(sizeof(*bo), GFP_KERNEL);
7740609d48SEric Anholt if (!bo)
784ff22f48SThomas Zimmermann return ERR_PTR(-ENOMEM);
7940609d48SEric Anholt obj = &bo->base.base;
8040609d48SEric Anholt
8140609d48SEric Anholt obj->funcs = &v3d_gem_funcs;
820cf2ef46SThomas Zimmermann bo->base.map_wc = true;
8340609d48SEric Anholt INIT_LIST_HEAD(&bo->unref_head);
8440609d48SEric Anholt
8540609d48SEric Anholt return &bo->base.base;
8657692c94SEric Anholt }
8757692c94SEric Anholt
8840609d48SEric Anholt static int
v3d_bo_create_finish(struct drm_gem_object * obj)8940609d48SEric Anholt v3d_bo_create_finish(struct drm_gem_object *obj)
9057692c94SEric Anholt {
9140609d48SEric Anholt struct v3d_dev *v3d = to_v3d_dev(obj->dev);
9257692c94SEric Anholt struct v3d_bo *bo = to_v3d_bo(obj);
9340609d48SEric Anholt struct sg_table *sgt;
9457692c94SEric Anholt int ret;
9557692c94SEric Anholt
9640609d48SEric Anholt /* So far we pin the BO in the MMU for its lifetime, so use
9740609d48SEric Anholt * shmem's helper for getting a lifetime sgt.
9840609d48SEric Anholt */
99a193f3b4SThomas Zimmermann sgt = drm_gem_shmem_get_pages_sgt(&bo->base);
10040609d48SEric Anholt if (IS_ERR(sgt))
10140609d48SEric Anholt return PTR_ERR(sgt);
10240609d48SEric Anholt
10340609d48SEric Anholt spin_lock(&v3d->mm_lock);
10440609d48SEric Anholt /* Allocate the object's space in the GPU's page tables.
10540609d48SEric Anholt * Inserting PTEs will happen later, but the offset is for the
10640609d48SEric Anholt * lifetime of the BO.
10740609d48SEric Anholt */
10840609d48SEric Anholt ret = drm_mm_insert_node_generic(&v3d->mm, &bo->node,
10940609d48SEric Anholt obj->size >> PAGE_SHIFT,
11040609d48SEric Anholt GMP_GRANULARITY >> PAGE_SHIFT, 0, 0);
11140609d48SEric Anholt spin_unlock(&v3d->mm_lock);
11257692c94SEric Anholt if (ret)
11357692c94SEric Anholt return ret;
11457692c94SEric Anholt
11540609d48SEric Anholt /* Track stats for /debug/dri/n/bo_stats. */
11640609d48SEric Anholt mutex_lock(&v3d->bo_lock);
11740609d48SEric Anholt v3d->bo_stats.num_allocated++;
11840609d48SEric Anholt v3d->bo_stats.pages_allocated += obj->size >> PAGE_SHIFT;
11940609d48SEric Anholt mutex_unlock(&v3d->bo_lock);
12057692c94SEric Anholt
12140609d48SEric Anholt v3d_mmu_insert_ptes(bo);
12257692c94SEric Anholt
12357692c94SEric Anholt return 0;
12457692c94SEric Anholt }
12557692c94SEric Anholt
v3d_bo_create(struct drm_device * dev,struct drm_file * file_priv,size_t unaligned_size)12640609d48SEric Anholt struct v3d_bo *v3d_bo_create(struct drm_device *dev, struct drm_file *file_priv,
12740609d48SEric Anholt size_t unaligned_size)
12857692c94SEric Anholt {
12940609d48SEric Anholt struct drm_gem_shmem_object *shmem_obj;
13040609d48SEric Anholt struct v3d_bo *bo;
13140609d48SEric Anholt int ret;
13257692c94SEric Anholt
13340609d48SEric Anholt shmem_obj = drm_gem_shmem_create(dev, unaligned_size);
134fc0c77fcSDan Carpenter if (IS_ERR(shmem_obj))
135fc0c77fcSDan Carpenter return ERR_CAST(shmem_obj);
13640609d48SEric Anholt bo = to_v3d_bo(&shmem_obj->base);
13740609d48SEric Anholt
13840609d48SEric Anholt ret = v3d_bo_create_finish(&shmem_obj->base);
13940609d48SEric Anholt if (ret)
14040609d48SEric Anholt goto free_obj;
14140609d48SEric Anholt
14240609d48SEric Anholt return bo;
14340609d48SEric Anholt
14440609d48SEric Anholt free_obj:
145a193f3b4SThomas Zimmermann drm_gem_shmem_free(shmem_obj);
14640609d48SEric Anholt return ERR_PTR(ret);
14757692c94SEric Anholt }
14857692c94SEric Anholt
14957692c94SEric Anholt struct drm_gem_object *
v3d_prime_import_sg_table(struct drm_device * dev,struct dma_buf_attachment * attach,struct sg_table * sgt)15057692c94SEric Anholt v3d_prime_import_sg_table(struct drm_device *dev,
15157692c94SEric Anholt struct dma_buf_attachment *attach,
15257692c94SEric Anholt struct sg_table *sgt)
15357692c94SEric Anholt {
15457692c94SEric Anholt struct drm_gem_object *obj;
15540609d48SEric Anholt int ret;
15657692c94SEric Anholt
15740609d48SEric Anholt obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
15840609d48SEric Anholt if (IS_ERR(obj))
15940609d48SEric Anholt return obj;
16057692c94SEric Anholt
16140609d48SEric Anholt ret = v3d_bo_create_finish(obj);
16240609d48SEric Anholt if (ret) {
163a193f3b4SThomas Zimmermann drm_gem_shmem_free(&to_v3d_bo(obj)->base);
16440609d48SEric Anholt return ERR_PTR(ret);
16540609d48SEric Anholt }
16657692c94SEric Anholt
16757692c94SEric Anholt return obj;
16857692c94SEric Anholt }
16957692c94SEric Anholt
v3d_create_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)17057692c94SEric Anholt int v3d_create_bo_ioctl(struct drm_device *dev, void *data,
17157692c94SEric Anholt struct drm_file *file_priv)
17257692c94SEric Anholt {
17357692c94SEric Anholt struct drm_v3d_create_bo *args = data;
17457692c94SEric Anholt struct v3d_bo *bo = NULL;
17557692c94SEric Anholt int ret;
17657692c94SEric Anholt
17757692c94SEric Anholt if (args->flags != 0) {
17857692c94SEric Anholt DRM_INFO("unknown create_bo flags: %d\n", args->flags);
17957692c94SEric Anholt return -EINVAL;
18057692c94SEric Anholt }
18157692c94SEric Anholt
18257692c94SEric Anholt bo = v3d_bo_create(dev, file_priv, PAGE_ALIGN(args->size));
18357692c94SEric Anholt if (IS_ERR(bo))
18457692c94SEric Anholt return PTR_ERR(bo);
18557692c94SEric Anholt
18657692c94SEric Anholt args->offset = bo->node.start << PAGE_SHIFT;
18757692c94SEric Anholt
18840609d48SEric Anholt ret = drm_gem_handle_create(file_priv, &bo->base.base, &args->handle);
1892b86189eSEmil Velikov drm_gem_object_put(&bo->base.base);
19057692c94SEric Anholt
19157692c94SEric Anholt return ret;
19257692c94SEric Anholt }
19357692c94SEric Anholt
v3d_mmap_bo_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)19457692c94SEric Anholt int v3d_mmap_bo_ioctl(struct drm_device *dev, void *data,
19557692c94SEric Anholt struct drm_file *file_priv)
19657692c94SEric Anholt {
19757692c94SEric Anholt struct drm_v3d_mmap_bo *args = data;
19857692c94SEric Anholt struct drm_gem_object *gem_obj;
19957692c94SEric Anholt
20057692c94SEric Anholt if (args->flags != 0) {
20157692c94SEric Anholt DRM_INFO("unknown mmap_bo flags: %d\n", args->flags);
20257692c94SEric Anholt return -EINVAL;
20357692c94SEric Anholt }
20457692c94SEric Anholt
20557692c94SEric Anholt gem_obj = drm_gem_object_lookup(file_priv, args->handle);
20657692c94SEric Anholt if (!gem_obj) {
20757692c94SEric Anholt DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
20857692c94SEric Anholt return -ENOENT;
20957692c94SEric Anholt }
21057692c94SEric Anholt
21157692c94SEric Anholt args->offset = drm_vma_node_offset_addr(&gem_obj->vma_node);
2122b86189eSEmil Velikov drm_gem_object_put(gem_obj);
21357692c94SEric Anholt
21440609d48SEric Anholt return 0;
21557692c94SEric Anholt }
21657692c94SEric Anholt
v3d_get_bo_offset_ioctl(struct drm_device * dev,void * data,struct drm_file * file_priv)21757692c94SEric Anholt int v3d_get_bo_offset_ioctl(struct drm_device *dev, void *data,
21857692c94SEric Anholt struct drm_file *file_priv)
21957692c94SEric Anholt {
22057692c94SEric Anholt struct drm_v3d_get_bo_offset *args = data;
22157692c94SEric Anholt struct drm_gem_object *gem_obj;
22257692c94SEric Anholt struct v3d_bo *bo;
22357692c94SEric Anholt
22457692c94SEric Anholt gem_obj = drm_gem_object_lookup(file_priv, args->handle);
22557692c94SEric Anholt if (!gem_obj) {
22657692c94SEric Anholt DRM_DEBUG("Failed to look up GEM BO %d\n", args->handle);
22757692c94SEric Anholt return -ENOENT;
22857692c94SEric Anholt }
22957692c94SEric Anholt bo = to_v3d_bo(gem_obj);
23057692c94SEric Anholt
23157692c94SEric Anholt args->offset = bo->node.start << PAGE_SHIFT;
23257692c94SEric Anholt
2332b86189eSEmil Velikov drm_gem_object_put(gem_obj);
23457692c94SEric Anholt return 0;
23557692c94SEric Anholt }
236