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