18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright 2019 Linaro, Ltd, Rob Herring <robh@kernel.org> */
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/err.h>
58c2ecf20Sopenharmony_ci#include <linux/slab.h>
68c2ecf20Sopenharmony_ci#include <linux/dma-buf.h>
78c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <drm/panfrost_drm.h>
108c2ecf20Sopenharmony_ci#include "panfrost_device.h"
118c2ecf20Sopenharmony_ci#include "panfrost_gem.h"
128c2ecf20Sopenharmony_ci#include "panfrost_mmu.h"
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci/* Called DRM core on the last userspace/kernel unreference of the
158c2ecf20Sopenharmony_ci * BO.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_cistatic void panfrost_gem_free_object(struct drm_gem_object *obj)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct panfrost_gem_object *bo = to_panfrost_bo(obj);
208c2ecf20Sopenharmony_ci	struct panfrost_device *pfdev = obj->dev->dev_private;
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci	/*
238c2ecf20Sopenharmony_ci	 * Make sure the BO is no longer inserted in the shrinker list before
248c2ecf20Sopenharmony_ci	 * taking care of the destruction itself. If we don't do that we have a
258c2ecf20Sopenharmony_ci	 * race condition between this function and what's done in
268c2ecf20Sopenharmony_ci	 * panfrost_gem_shrinker_scan().
278c2ecf20Sopenharmony_ci	 */
288c2ecf20Sopenharmony_ci	mutex_lock(&pfdev->shrinker_lock);
298c2ecf20Sopenharmony_ci	list_del_init(&bo->base.madv_list);
308c2ecf20Sopenharmony_ci	mutex_unlock(&pfdev->shrinker_lock);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	/*
338c2ecf20Sopenharmony_ci	 * If we still have mappings attached to the BO, there's a problem in
348c2ecf20Sopenharmony_ci	 * our refcounting.
358c2ecf20Sopenharmony_ci	 */
368c2ecf20Sopenharmony_ci	WARN_ON_ONCE(!list_empty(&bo->mappings.list));
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (bo->sgts) {
398c2ecf20Sopenharmony_ci		int i;
408c2ecf20Sopenharmony_ci		int n_sgt = bo->base.base.size / SZ_2M;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci		for (i = 0; i < n_sgt; i++) {
438c2ecf20Sopenharmony_ci			if (bo->sgts[i].sgl) {
448c2ecf20Sopenharmony_ci				dma_unmap_sgtable(pfdev->dev, &bo->sgts[i],
458c2ecf20Sopenharmony_ci						  DMA_BIDIRECTIONAL, 0);
468c2ecf20Sopenharmony_ci				sg_free_table(&bo->sgts[i]);
478c2ecf20Sopenharmony_ci			}
488c2ecf20Sopenharmony_ci		}
498c2ecf20Sopenharmony_ci		kvfree(bo->sgts);
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	drm_gem_shmem_free_object(obj);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct panfrost_gem_mapping *
568c2ecf20Sopenharmony_cipanfrost_gem_mapping_get(struct panfrost_gem_object *bo,
578c2ecf20Sopenharmony_ci			 struct panfrost_file_priv *priv)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct panfrost_gem_mapping *iter, *mapping = NULL;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	mutex_lock(&bo->mappings.lock);
628c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &bo->mappings.list, node) {
638c2ecf20Sopenharmony_ci		if (iter->mmu == priv->mmu) {
648c2ecf20Sopenharmony_ci			kref_get(&iter->refcount);
658c2ecf20Sopenharmony_ci			mapping = iter;
668c2ecf20Sopenharmony_ci			break;
678c2ecf20Sopenharmony_ci		}
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci	mutex_unlock(&bo->mappings.lock);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	return mapping;
728c2ecf20Sopenharmony_ci}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic void
758c2ecf20Sopenharmony_cipanfrost_gem_teardown_mapping(struct panfrost_gem_mapping *mapping)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	if (mapping->active)
788c2ecf20Sopenharmony_ci		panfrost_mmu_unmap(mapping);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	spin_lock(&mapping->mmu->mm_lock);
818c2ecf20Sopenharmony_ci	if (drm_mm_node_allocated(&mapping->mmnode))
828c2ecf20Sopenharmony_ci		drm_mm_remove_node(&mapping->mmnode);
838c2ecf20Sopenharmony_ci	spin_unlock(&mapping->mmu->mm_lock);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic void panfrost_gem_mapping_release(struct kref *kref)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct panfrost_gem_mapping *mapping;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	mapping = container_of(kref, struct panfrost_gem_mapping, refcount);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	panfrost_gem_teardown_mapping(mapping);
938c2ecf20Sopenharmony_ci	drm_gem_object_put(&mapping->obj->base.base);
948c2ecf20Sopenharmony_ci	panfrost_mmu_ctx_put(mapping->mmu);
958c2ecf20Sopenharmony_ci	kfree(mapping);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_civoid panfrost_gem_mapping_put(struct panfrost_gem_mapping *mapping)
998c2ecf20Sopenharmony_ci{
1008c2ecf20Sopenharmony_ci	if (!mapping)
1018c2ecf20Sopenharmony_ci		return;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	kref_put(&mapping->refcount, panfrost_gem_mapping_release);
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_civoid panfrost_gem_teardown_mappings_locked(struct panfrost_gem_object *bo)
1078c2ecf20Sopenharmony_ci{
1088c2ecf20Sopenharmony_ci	struct panfrost_gem_mapping *mapping;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	list_for_each_entry(mapping, &bo->mappings.list, node)
1118c2ecf20Sopenharmony_ci		panfrost_gem_teardown_mapping(mapping);
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ciint panfrost_gem_open(struct drm_gem_object *obj, struct drm_file *file_priv)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	int ret;
1178c2ecf20Sopenharmony_ci	size_t size = obj->size;
1188c2ecf20Sopenharmony_ci	u64 align;
1198c2ecf20Sopenharmony_ci	struct panfrost_gem_object *bo = to_panfrost_bo(obj);
1208c2ecf20Sopenharmony_ci	unsigned long color = bo->noexec ? PANFROST_BO_NOEXEC : 0;
1218c2ecf20Sopenharmony_ci	struct panfrost_file_priv *priv = file_priv->driver_priv;
1228c2ecf20Sopenharmony_ci	struct panfrost_gem_mapping *mapping;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
1258c2ecf20Sopenharmony_ci	if (!mapping)
1268c2ecf20Sopenharmony_ci		return -ENOMEM;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&mapping->node);
1298c2ecf20Sopenharmony_ci	kref_init(&mapping->refcount);
1308c2ecf20Sopenharmony_ci	drm_gem_object_get(obj);
1318c2ecf20Sopenharmony_ci	mapping->obj = bo;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	/*
1348c2ecf20Sopenharmony_ci	 * Executable buffers cannot cross a 16MB boundary as the program
1358c2ecf20Sopenharmony_ci	 * counter is 24-bits. We assume executable buffers will be less than
1368c2ecf20Sopenharmony_ci	 * 16MB and aligning executable buffers to their size will avoid
1378c2ecf20Sopenharmony_ci	 * crossing a 16MB boundary.
1388c2ecf20Sopenharmony_ci	 */
1398c2ecf20Sopenharmony_ci	if (!bo->noexec)
1408c2ecf20Sopenharmony_ci		align = size >> PAGE_SHIFT;
1418c2ecf20Sopenharmony_ci	else
1428c2ecf20Sopenharmony_ci		align = size >= SZ_2M ? SZ_2M >> PAGE_SHIFT : 0;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	mapping->mmu = panfrost_mmu_ctx_get(priv->mmu);
1458c2ecf20Sopenharmony_ci	spin_lock(&mapping->mmu->mm_lock);
1468c2ecf20Sopenharmony_ci	ret = drm_mm_insert_node_generic(&mapping->mmu->mm, &mapping->mmnode,
1478c2ecf20Sopenharmony_ci					 size >> PAGE_SHIFT, align, color, 0);
1488c2ecf20Sopenharmony_ci	spin_unlock(&mapping->mmu->mm_lock);
1498c2ecf20Sopenharmony_ci	if (ret)
1508c2ecf20Sopenharmony_ci		goto err;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (!bo->is_heap) {
1538c2ecf20Sopenharmony_ci		ret = panfrost_mmu_map(mapping);
1548c2ecf20Sopenharmony_ci		if (ret)
1558c2ecf20Sopenharmony_ci			goto err;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	mutex_lock(&bo->mappings.lock);
1598c2ecf20Sopenharmony_ci	WARN_ON(bo->base.madv != PANFROST_MADV_WILLNEED);
1608c2ecf20Sopenharmony_ci	list_add_tail(&mapping->node, &bo->mappings.list);
1618c2ecf20Sopenharmony_ci	mutex_unlock(&bo->mappings.lock);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_cierr:
1648c2ecf20Sopenharmony_ci	if (ret)
1658c2ecf20Sopenharmony_ci		panfrost_gem_mapping_put(mapping);
1668c2ecf20Sopenharmony_ci	return ret;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_civoid panfrost_gem_close(struct drm_gem_object *obj, struct drm_file *file_priv)
1708c2ecf20Sopenharmony_ci{
1718c2ecf20Sopenharmony_ci	struct panfrost_file_priv *priv = file_priv->driver_priv;
1728c2ecf20Sopenharmony_ci	struct panfrost_gem_object *bo = to_panfrost_bo(obj);
1738c2ecf20Sopenharmony_ci	struct panfrost_gem_mapping *mapping = NULL, *iter;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	mutex_lock(&bo->mappings.lock);
1768c2ecf20Sopenharmony_ci	list_for_each_entry(iter, &bo->mappings.list, node) {
1778c2ecf20Sopenharmony_ci		if (iter->mmu == priv->mmu) {
1788c2ecf20Sopenharmony_ci			mapping = iter;
1798c2ecf20Sopenharmony_ci			list_del(&iter->node);
1808c2ecf20Sopenharmony_ci			break;
1818c2ecf20Sopenharmony_ci		}
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci	mutex_unlock(&bo->mappings.lock);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	panfrost_gem_mapping_put(mapping);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic int panfrost_gem_pin(struct drm_gem_object *obj)
1898c2ecf20Sopenharmony_ci{
1908c2ecf20Sopenharmony_ci	if (to_panfrost_bo(obj)->is_heap)
1918c2ecf20Sopenharmony_ci		return -EINVAL;
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	return drm_gem_shmem_pin(obj);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic const struct drm_gem_object_funcs panfrost_gem_funcs = {
1978c2ecf20Sopenharmony_ci	.free = panfrost_gem_free_object,
1988c2ecf20Sopenharmony_ci	.open = panfrost_gem_open,
1998c2ecf20Sopenharmony_ci	.close = panfrost_gem_close,
2008c2ecf20Sopenharmony_ci	.print_info = drm_gem_shmem_print_info,
2018c2ecf20Sopenharmony_ci	.pin = panfrost_gem_pin,
2028c2ecf20Sopenharmony_ci	.unpin = drm_gem_shmem_unpin,
2038c2ecf20Sopenharmony_ci	.get_sg_table = drm_gem_shmem_get_sg_table,
2048c2ecf20Sopenharmony_ci	.vmap = drm_gem_shmem_vmap,
2058c2ecf20Sopenharmony_ci	.vunmap = drm_gem_shmem_vunmap,
2068c2ecf20Sopenharmony_ci	.mmap = drm_gem_shmem_mmap,
2078c2ecf20Sopenharmony_ci};
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci/**
2108c2ecf20Sopenharmony_ci * panfrost_gem_create_object - Implementation of driver->gem_create_object.
2118c2ecf20Sopenharmony_ci * @dev: DRM device
2128c2ecf20Sopenharmony_ci * @size: Size in bytes of the memory the object will reference
2138c2ecf20Sopenharmony_ci *
2148c2ecf20Sopenharmony_ci * This lets the GEM helpers allocate object structs for us, and keep
2158c2ecf20Sopenharmony_ci * our BO stats correct.
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_cistruct drm_gem_object *panfrost_gem_create_object(struct drm_device *dev, size_t size)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct panfrost_device *pfdev = dev->dev_private;
2208c2ecf20Sopenharmony_ci	struct panfrost_gem_object *obj;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	obj = kzalloc(sizeof(*obj), GFP_KERNEL);
2238c2ecf20Sopenharmony_ci	if (!obj)
2248c2ecf20Sopenharmony_ci		return NULL;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&obj->mappings.list);
2278c2ecf20Sopenharmony_ci	mutex_init(&obj->mappings.lock);
2288c2ecf20Sopenharmony_ci	obj->base.base.funcs = &panfrost_gem_funcs;
2298c2ecf20Sopenharmony_ci	obj->base.map_cached = pfdev->coherent;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	return &obj->base.base;
2328c2ecf20Sopenharmony_ci}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_cistruct panfrost_gem_object *
2358c2ecf20Sopenharmony_cipanfrost_gem_create(struct drm_device *dev, size_t size, u32 flags)
2368c2ecf20Sopenharmony_ci{
2378c2ecf20Sopenharmony_ci	struct drm_gem_shmem_object *shmem;
2388c2ecf20Sopenharmony_ci	struct panfrost_gem_object *bo;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	/* Round up heap allocations to 2MB to keep fault handling simple */
2418c2ecf20Sopenharmony_ci	if (flags & PANFROST_BO_HEAP)
2428c2ecf20Sopenharmony_ci		size = roundup(size, SZ_2M);
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	shmem = drm_gem_shmem_create(dev, size);
2458c2ecf20Sopenharmony_ci	if (IS_ERR(shmem))
2468c2ecf20Sopenharmony_ci		return ERR_CAST(shmem);
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	bo = to_panfrost_bo(&shmem->base);
2498c2ecf20Sopenharmony_ci	bo->noexec = !!(flags & PANFROST_BO_NOEXEC);
2508c2ecf20Sopenharmony_ci	bo->is_heap = !!(flags & PANFROST_BO_HEAP);
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return bo;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistruct drm_gem_object *
2568c2ecf20Sopenharmony_cipanfrost_gem_prime_import_sg_table(struct drm_device *dev,
2578c2ecf20Sopenharmony_ci				   struct dma_buf_attachment *attach,
2588c2ecf20Sopenharmony_ci				   struct sg_table *sgt)
2598c2ecf20Sopenharmony_ci{
2608c2ecf20Sopenharmony_ci	struct drm_gem_object *obj;
2618c2ecf20Sopenharmony_ci	struct panfrost_gem_object *bo;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	obj = drm_gem_shmem_prime_import_sg_table(dev, attach, sgt);
2648c2ecf20Sopenharmony_ci	if (IS_ERR(obj))
2658c2ecf20Sopenharmony_ci		return ERR_CAST(obj);
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	bo = to_panfrost_bo(obj);
2688c2ecf20Sopenharmony_ci	bo->noexec = true;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return obj;
2718c2ecf20Sopenharmony_ci}
272