xref: /openbmc/linux/drivers/gpu/drm/v3d/v3d_bo.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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