From fda6ccdbd4930361a5a1dce1d2adf0998f63441e Mon Sep 17 00:00:00 2001
From: Lima Project Developers <dri-devel@lists.freedesktop.org>
Date: Wed, 16 May 2018 10:34:29 +0800
Subject: [PATCH 056/146] drm/lima: add GEM related functions

Signed-off-by: Qiang Yu <yuq825@gmail.com>
Signed-off-by: Erico Nunes <nunes.erico@gmail.com>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
 drivers/gpu/drm/lima/lima_gem.c | 459 ++++++++++++++++++++++++++++++++
 drivers/gpu/drm/lima/lima_gem.h |  41 +++
 2 files changed, 500 insertions(+)
 create mode 100644 drivers/gpu/drm/lima/lima_gem.c
 create mode 100644 drivers/gpu/drm/lima/lima_gem.h

diff --git a/drivers/gpu/drm/lima/lima_gem.c b/drivers/gpu/drm/lima/lima_gem.c
new file mode 100644
index 000000000000..1ad3f38ddfde
--- /dev/null
+++ b/drivers/gpu/drm/lima/lima_gem.c
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2017-2018 Lima Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <drm/drmP.h>
+#include <linux/dma-mapping.h>
+#include <linux/pagemap.h>
+#include <linux/sync_file.h>
+
+#include <drm/lima_drm.h>
+
+#include "lima_drv.h"
+#include "lima_gem.h"
+#include "lima_vm.h"
+#include "lima_object.h"
+
+int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
+			   u32 size, u32 flags, u32 *handle)
+{
+	int err;
+	struct lima_bo *bo;
+	struct lima_device *ldev = to_lima_dev(dev);
+
+	bo = lima_bo_create(ldev, size, flags, ttm_bo_type_device, NULL, NULL);
+	if (IS_ERR(bo))
+		return PTR_ERR(bo);
+
+	err = drm_gem_handle_create(file, &bo->gem, handle);
+
+	/* drop reference from allocate - handle holds it now */
+	drm_gem_object_put_unlocked(&bo->gem);
+
+	return err;
+}
+
+void lima_gem_free_object(struct drm_gem_object *obj)
+{
+	struct lima_bo *bo = to_lima_bo(obj);
+
+	if (!list_empty(&bo->va))
+		dev_err(obj->dev->dev, "lima gem free bo still has va\n");
+
+	lima_bo_unref(bo);
+}
+
+int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file)
+{
+	struct lima_bo *bo = to_lima_bo(obj);
+	struct lima_drm_priv *priv = to_lima_drm_priv(file);
+	struct lima_vm *vm = priv->vm;
+	int err;
+
+	err = lima_bo_reserve(bo, true);
+	if (err)
+		return err;
+
+	err = lima_vm_bo_add(vm, bo);
+
+	lima_bo_unreserve(bo);
+	return err;
+}
+
+void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file)
+{
+	struct lima_bo *bo = to_lima_bo(obj);
+	struct lima_device *dev = to_lima_dev(obj->dev);
+	struct lima_drm_priv *priv = to_lima_drm_priv(file);
+	struct lima_vm *vm = priv->vm;
+
+	LIST_HEAD(list);
+	struct ttm_validate_buffer tv_bo, tv_pd;
+	struct ww_acquire_ctx ticket;
+	int r;
+
+	tv_bo.bo = &bo->tbo;
+	tv_bo.shared = true;
+	list_add(&tv_bo.head, &list);
+
+	tv_pd.bo = &vm->pd->tbo;
+	tv_pd.shared = true;
+	list_add(&tv_pd.head, &list);
+
+	r = ttm_eu_reserve_buffers(&ticket, &list, false, NULL);
+	if (r) {
+		dev_err(dev->dev, "leeking bo va because we "
+			"fail to reserve bo (%d)\n", r);
+		return;
+	}
+
+	lima_vm_bo_del(vm, bo);
+
+	ttm_eu_backoff_reservation(&ticket, &list);
+}
+
+int lima_gem_mmap_offset(struct drm_file *file, u32 handle, u64 *offset)
+{
+	struct drm_gem_object *obj;
+	struct lima_bo *bo;
+
+	obj = drm_gem_object_lookup(file, handle);
+	if (!obj)
+		return -ENOENT;
+
+	bo = to_lima_bo(obj);
+	*offset = drm_vma_node_offset_addr(&bo->tbo.vma_node);
+
+	drm_gem_object_put_unlocked(obj);
+	return 0;
+}
+
+int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma)
+{
+	struct drm_file *file_priv;
+	struct lima_device *dev;
+
+	if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
+		return -EINVAL;
+
+	file_priv = filp->private_data;
+	dev = file_priv->minor->dev->dev_private;
+	if (dev == NULL)
+		return -EINVAL;
+
+	return ttm_bo_mmap(filp, vma, &dev->mman.bdev);
+}
+
+int lima_gem_va_map(struct drm_file *file, u32 handle, u32 flags, u32 va)
+{
+	struct lima_drm_priv *priv = to_lima_drm_priv(file);
+	struct lima_vm *vm = priv->vm;
+	struct drm_gem_object *obj;
+	struct lima_bo *bo;
+	struct lima_device *dev;
+	int err;
+
+	LIST_HEAD(list);
+	struct ttm_validate_buffer tv_bo, tv_pd;
+	struct ww_acquire_ctx ticket;
+
+	if (!PAGE_ALIGNED(va))
+		return -EINVAL;
+
+	obj = drm_gem_object_lookup(file, handle);
+	if (!obj)
+		return -ENOENT;
+
+	bo = to_lima_bo(obj);
+	dev = to_lima_dev(obj->dev);
+
+	/* carefully handle overflow when calculate range */
+	if (va < dev->va_start || dev->va_end - obj->size < va) {
+		err = -EINVAL;
+		goto out;
+	}
+
+	tv_bo.bo = &bo->tbo;
+	tv_bo.shared = true;
+	list_add(&tv_bo.head, &list);
+
+	tv_pd.bo = &vm->pd->tbo;
+	tv_pd.shared = true;
+	list_add(&tv_pd.head, &list);
+
+	err = ttm_eu_reserve_buffers(&ticket, &list, false, NULL);
+	if (err)
+		goto out;
+
+	err = lima_vm_bo_map(vm, bo, va);
+
+	ttm_eu_backoff_reservation(&ticket, &list);
+out:
+	drm_gem_object_put_unlocked(obj);
+	return err;
+}
+
+int lima_gem_va_unmap(struct drm_file *file, u32 handle, u32 va)
+{
+	struct lima_drm_priv *priv = to_lima_drm_priv(file);
+	struct lima_vm *vm = priv->vm;
+	struct drm_gem_object *obj;
+	struct lima_bo *bo;
+	int err;
+
+	LIST_HEAD(list);
+	struct ttm_validate_buffer tv_bo, tv_pd;
+	struct ww_acquire_ctx ticket;
+
+	if (!PAGE_ALIGNED(va))
+		return -EINVAL;
+
+	obj = drm_gem_object_lookup(file, handle);
+	if (!obj)
+		return -ENOENT;
+
+	bo = to_lima_bo(obj);
+
+	tv_bo.bo = &bo->tbo;
+	tv_bo.shared = true;
+	list_add(&tv_bo.head, &list);
+
+	tv_pd.bo = &vm->pd->tbo;
+	tv_pd.shared = true;
+	list_add(&tv_pd.head, &list);
+
+	err = ttm_eu_reserve_buffers(&ticket, &list, false, NULL);
+	if (err)
+		goto out;
+
+	err = lima_vm_bo_unmap(vm, bo, va);
+
+	ttm_eu_backoff_reservation(&ticket, &list);
+out:
+	drm_gem_object_put_unlocked(obj);
+	return err;
+}
+
+static int lima_gem_sync_bo(struct lima_sched_task *task, struct lima_bo *bo,
+			    bool write, bool explicit)
+{
+	int i, err;
+	struct dma_fence *f;
+	u64 context = task->base.s_fence->finished.context;
+
+	if (!write) {
+		err = reservation_object_reserve_shared(bo->tbo.resv);
+		if (err)
+			return err;
+	}
+
+	/* explicit sync use user passed dep fence */
+	if (explicit)
+		return 0;
+
+	/* implicit sync use bo fence in resv obj */
+	if (write) {
+		struct reservation_object_list *fobj =
+			reservation_object_get_list(bo->tbo.resv);
+
+		if (fobj && fobj->shared_count > 0) {
+			for (i = 0; i < fobj->shared_count; i++) {
+				f = rcu_dereference_protected(
+					fobj->shared[i],
+					reservation_object_held(bo->tbo.resv));
+				if (f->context != context) {
+					err = lima_sched_task_add_dep(task, f);
+					if (err)
+						return err;
+				}
+			}
+		}
+	}
+
+	f = reservation_object_get_excl(bo->tbo.resv);
+	if (f) {
+		err = lima_sched_task_add_dep(task, f);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+static int lima_gem_add_deps(struct lima_ctx_mgr *mgr, struct lima_submit *submit)
+{
+	int i, err = 0;
+
+	for (i = 0; i < submit->nr_deps; i++) {
+		union drm_lima_gem_submit_dep *dep = submit->deps + i;
+		struct dma_fence *fence;
+
+		if (dep->type == LIMA_SUBMIT_DEP_FENCE) {
+			fence = lima_ctx_get_native_fence(
+				mgr, dep->fence.ctx, dep->fence.pipe,
+				dep->fence.seq);
+			if (IS_ERR(fence)) {
+				err = PTR_ERR(fence);
+				break;
+			}
+		}
+		else if (dep->type == LIMA_SUBMIT_DEP_SYNC_FD) {
+			fence = sync_file_get_fence(dep->sync_fd.fd);
+			if (!fence) {
+				err = -EINVAL;
+				break;
+			}
+		}
+		else {
+			err = -EINVAL;
+			break;
+		}
+
+		if (fence) {
+			err = lima_sched_task_add_dep(submit->task, fence);
+			dma_fence_put(fence);
+			if (err)
+				break;
+		}
+	}
+
+	return err;
+}
+
+static int lima_gem_get_sync_fd(struct dma_fence *fence)
+{
+	struct sync_file *sync_file;
+	int fd;
+
+	fd = get_unused_fd_flags(O_CLOEXEC);
+	if (fd < 0)
+		return fd;
+
+	sync_file = sync_file_create(fence);
+	if (!sync_file) {
+		put_unused_fd(fd);
+		return -ENOMEM;
+	}
+
+	fd_install(fd, sync_file->file);
+	return fd;
+}
+
+int lima_gem_submit(struct drm_file *file, struct lima_submit *submit)
+{
+	int i, err = 0;
+	struct lima_drm_priv *priv = to_lima_drm_priv(file);
+	struct lima_vm *vm = priv->vm;
+
+	INIT_LIST_HEAD(&submit->validated);
+	INIT_LIST_HEAD(&submit->duplicates);
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct drm_gem_object *obj;
+		struct drm_lima_gem_submit_bo *bo = submit->bos + i;
+		struct ttm_validate_buffer *vb = submit->vbs + i;
+
+		obj = drm_gem_object_lookup(file, bo->handle);
+		if (!obj) {
+			err = -ENOENT;
+			goto out0;
+		}
+
+		vb->bo = &to_lima_bo(obj)->tbo;
+		vb->shared = !(bo->flags & LIMA_SUBMIT_BO_WRITE);
+		list_add_tail(&vb->head, &submit->validated);
+	}
+
+	submit->vm_pd_vb.bo = &vm->pd->tbo;
+	submit->vm_pd_vb.shared = true;
+	list_add(&submit->vm_pd_vb.head, &submit->validated);
+
+	err = ttm_eu_reserve_buffers(&submit->ticket, &submit->validated,
+				     true, &submit->duplicates);
+	if (err)
+		goto out0;
+
+	err = lima_sched_task_init(
+		submit->task, submit->ctx->context + submit->pipe, vm);
+	if (err)
+		goto out1;
+
+	err = lima_gem_add_deps(&priv->ctx_mgr, submit);
+	if (err)
+		goto out2;
+
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct ttm_validate_buffer *vb = submit->vbs + i;
+		struct lima_bo *bo = ttm_to_lima_bo(vb->bo);
+		err = lima_gem_sync_bo(
+			submit->task, bo, !vb->shared,
+			submit->flags & LIMA_SUBMIT_FLAG_EXPLICIT_FENCE);
+		if (err)
+			goto out2;
+	}
+
+	if (submit->flags & LIMA_SUBMIT_FLAG_SYNC_FD_OUT) {
+		int fd = lima_gem_get_sync_fd(
+			&submit->task->base.s_fence->finished);
+		if (fd < 0) {
+			err = fd;
+			goto out2;
+		}
+		submit->sync_fd = fd;
+	}
+
+	submit->fence = lima_sched_context_queue_task(
+		submit->ctx->context + submit->pipe, submit->task,
+		&submit->done);
+
+	ttm_eu_fence_buffer_objects(&submit->ticket, &submit->validated,
+				    &submit->task->base.s_fence->finished);
+
+out2:
+	if (err)
+		lima_sched_task_fini(submit->task);
+out1:
+        if (err)
+		ttm_eu_backoff_reservation(&submit->ticket, &submit->validated);
+out0:
+	for (i = 0; i < submit->nr_bos; i++) {
+		struct ttm_validate_buffer *vb = submit->vbs + i;
+		if (!vb->bo)
+			break;
+		drm_gem_object_put_unlocked(&ttm_to_lima_bo(vb->bo)->gem);
+	}
+	return err;
+}
+
+int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, u64 timeout_ns)
+{
+	bool write = op & LIMA_GEM_WAIT_WRITE;
+	struct drm_gem_object *obj;
+	struct lima_bo *bo;
+	signed long ret;
+	unsigned long timeout;
+
+	obj = drm_gem_object_lookup(file, handle);
+	if (!obj)
+		return -ENOENT;
+
+	bo = to_lima_bo(obj);
+
+	timeout = timeout_ns ? lima_timeout_to_jiffies(timeout_ns) : 0;
+
+	ret = lima_bo_reserve(bo, true);
+	if (ret)
+		goto out;
+
+	/* must use long for result check because in 64bit arch int
+	 * will overflow if timeout is too large and get <0 result
+	 */
+	ret = reservation_object_wait_timeout_rcu(bo->tbo.resv, write, true, timeout);
+	if (ret == 0)
+		ret = timeout ? -ETIMEDOUT : -EBUSY;
+	else if (ret > 0)
+		ret = 0;
+
+	lima_bo_unreserve(bo);
+out:
+	drm_gem_object_put_unlocked(obj);
+	return ret;
+}
diff --git a/drivers/gpu/drm/lima/lima_gem.h b/drivers/gpu/drm/lima/lima_gem.h
new file mode 100644
index 000000000000..8e3c4110825d
--- /dev/null
+++ b/drivers/gpu/drm/lima/lima_gem.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017-2018 Lima Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __LIMA_GEM_H__
+#define __LIMA_GEM_H__
+
+struct lima_bo;
+struct lima_submit;
+
+struct lima_bo *lima_gem_create_bo(struct drm_device *dev, u32 size, u32 flags);
+int lima_gem_create_handle(struct drm_device *dev, struct drm_file *file,
+			   u32 size, u32 flags, u32 *handle);
+void lima_gem_free_object(struct drm_gem_object *obj);
+int lima_gem_object_open(struct drm_gem_object *obj, struct drm_file *file);
+void lima_gem_object_close(struct drm_gem_object *obj, struct drm_file *file);
+int lima_gem_mmap_offset(struct drm_file *file, u32 handle, u64 *offset);
+int lima_gem_mmap(struct file *filp, struct vm_area_struct *vma);
+int lima_gem_va_map(struct drm_file *file, u32 handle, u32 flags, u32 va);
+int lima_gem_va_unmap(struct drm_file *file, u32 handle, u32 va);
+int lima_gem_submit(struct drm_file *file, struct lima_submit *submit);
+int lima_gem_wait(struct drm_file *file, u32 handle, u32 op, u64 timeout_ns);
+
+#endif
-- 
2.17.1

