18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* exynos_drm_gem.c 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (c) 2011 Samsung Electronics Co., Ltd. 58c2ecf20Sopenharmony_ci * Author: Inki Dae <inki.dae@samsung.com> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/dma-buf.h> 108c2ecf20Sopenharmony_ci#include <linux/pfn_t.h> 118c2ecf20Sopenharmony_ci#include <linux/shmem_fs.h> 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <drm/drm_prime.h> 148c2ecf20Sopenharmony_ci#include <drm/drm_vma_manager.h> 158c2ecf20Sopenharmony_ci#include <drm/exynos_drm.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "exynos_drm_drv.h" 188c2ecf20Sopenharmony_ci#include "exynos_drm_gem.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_cistatic int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem, bool kvmap) 218c2ecf20Sopenharmony_ci{ 228c2ecf20Sopenharmony_ci struct drm_device *dev = exynos_gem->base.dev; 238c2ecf20Sopenharmony_ci unsigned long attr = 0; 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci if (exynos_gem->dma_addr) { 268c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(to_dma_dev(dev), "already allocated.\n"); 278c2ecf20Sopenharmony_ci return 0; 288c2ecf20Sopenharmony_ci } 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci /* 318c2ecf20Sopenharmony_ci * if EXYNOS_BO_CONTIG, fully physically contiguous memory 328c2ecf20Sopenharmony_ci * region will be allocated else physically contiguous 338c2ecf20Sopenharmony_ci * as possible. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG)) 368c2ecf20Sopenharmony_ci attr |= DMA_ATTR_FORCE_CONTIGUOUS; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci /* 398c2ecf20Sopenharmony_ci * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping 408c2ecf20Sopenharmony_ci * else cachable mapping. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_ci if (exynos_gem->flags & EXYNOS_BO_WC || 438c2ecf20Sopenharmony_ci !(exynos_gem->flags & EXYNOS_BO_CACHABLE)) 448c2ecf20Sopenharmony_ci attr |= DMA_ATTR_WRITE_COMBINE; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* FBDev emulation requires kernel mapping */ 478c2ecf20Sopenharmony_ci if (!kvmap) 488c2ecf20Sopenharmony_ci attr |= DMA_ATTR_NO_KERNEL_MAPPING; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci exynos_gem->dma_attrs = attr; 518c2ecf20Sopenharmony_ci exynos_gem->cookie = dma_alloc_attrs(to_dma_dev(dev), exynos_gem->size, 528c2ecf20Sopenharmony_ci &exynos_gem->dma_addr, GFP_KERNEL, 538c2ecf20Sopenharmony_ci exynos_gem->dma_attrs); 548c2ecf20Sopenharmony_ci if (!exynos_gem->cookie) { 558c2ecf20Sopenharmony_ci DRM_DEV_ERROR(to_dma_dev(dev), "failed to allocate buffer.\n"); 568c2ecf20Sopenharmony_ci return -ENOMEM; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci if (kvmap) 608c2ecf20Sopenharmony_ci exynos_gem->kvaddr = exynos_gem->cookie; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(to_dma_dev(dev), "dma_addr(0x%lx), size(0x%lx)\n", 638c2ecf20Sopenharmony_ci (unsigned long)exynos_gem->dma_addr, exynos_gem->size); 648c2ecf20Sopenharmony_ci return 0; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct drm_device *dev = exynos_gem->base.dev; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci if (!exynos_gem->dma_addr) { 728c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr is invalid.\n"); 738c2ecf20Sopenharmony_ci return; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(dev->dev, "dma_addr(0x%lx), size(0x%lx)\n", 778c2ecf20Sopenharmony_ci (unsigned long)exynos_gem->dma_addr, exynos_gem->size); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, 808c2ecf20Sopenharmony_ci (dma_addr_t)exynos_gem->dma_addr, 818c2ecf20Sopenharmony_ci exynos_gem->dma_attrs); 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int exynos_drm_gem_handle_create(struct drm_gem_object *obj, 858c2ecf20Sopenharmony_ci struct drm_file *file_priv, 868c2ecf20Sopenharmony_ci unsigned int *handle) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int ret; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci /* 918c2ecf20Sopenharmony_ci * allocate a id of idr table where the obj is registered 928c2ecf20Sopenharmony_ci * and handle has the id what user can see. 938c2ecf20Sopenharmony_ci */ 948c2ecf20Sopenharmony_ci ret = drm_gem_handle_create(file_priv, obj, handle); 958c2ecf20Sopenharmony_ci if (ret) 968c2ecf20Sopenharmony_ci return ret; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "gem handle = 0x%x\n", *handle); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* drop reference from allocate - handle holds it now. */ 1018c2ecf20Sopenharmony_ci drm_gem_object_put(obj); 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci return 0; 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_civoid exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci struct drm_gem_object *obj = &exynos_gem->base; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "handle count = %d\n", 1118c2ecf20Sopenharmony_ci obj->handle_count); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci /* 1148c2ecf20Sopenharmony_ci * do not release memory region from exporter. 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * the region will be released by exporter 1178c2ecf20Sopenharmony_ci * once dmabuf's refcount becomes 0. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_ci if (obj->import_attach) 1208c2ecf20Sopenharmony_ci drm_prime_gem_destroy(obj, exynos_gem->sgt); 1218c2ecf20Sopenharmony_ci else 1228c2ecf20Sopenharmony_ci exynos_drm_free_buf(exynos_gem); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* release file pointer to gem object. */ 1258c2ecf20Sopenharmony_ci drm_gem_object_release(obj); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci kfree(exynos_gem); 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev, 1318c2ecf20Sopenharmony_ci unsigned long size) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem; 1348c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci exynos_gem = kzalloc(sizeof(*exynos_gem), GFP_KERNEL); 1388c2ecf20Sopenharmony_ci if (!exynos_gem) 1398c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci exynos_gem->size = size; 1428c2ecf20Sopenharmony_ci obj = &exynos_gem->base; 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci ret = drm_gem_object_init(dev, obj, size); 1458c2ecf20Sopenharmony_ci if (ret < 0) { 1468c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to initialize gem object\n"); 1478c2ecf20Sopenharmony_ci kfree(exynos_gem); 1488c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1498c2ecf20Sopenharmony_ci } 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci ret = drm_gem_create_mmap_offset(obj); 1528c2ecf20Sopenharmony_ci if (ret < 0) { 1538c2ecf20Sopenharmony_ci drm_gem_object_release(obj); 1548c2ecf20Sopenharmony_ci kfree(exynos_gem); 1558c2ecf20Sopenharmony_ci return ERR_PTR(ret); 1568c2ecf20Sopenharmony_ci } 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(dev->dev, "created file object = %pK\n", obj->filp); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return exynos_gem; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistruct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev, 1648c2ecf20Sopenharmony_ci unsigned int flags, 1658c2ecf20Sopenharmony_ci unsigned long size, 1668c2ecf20Sopenharmony_ci bool kvmap) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem; 1698c2ecf20Sopenharmony_ci int ret; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci if (flags & ~(EXYNOS_BO_MASK)) { 1728c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, 1738c2ecf20Sopenharmony_ci "invalid GEM buffer flags: %u\n", flags); 1748c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci if (!size) { 1788c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "invalid GEM buffer size: %lu\n", size); 1798c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci size = roundup(size, PAGE_SIZE); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci exynos_gem = exynos_drm_gem_init(dev, size); 1858c2ecf20Sopenharmony_ci if (IS_ERR(exynos_gem)) 1868c2ecf20Sopenharmony_ci return exynos_gem; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci if (!is_drm_iommu_supported(dev) && (flags & EXYNOS_BO_NONCONTIG)) { 1898c2ecf20Sopenharmony_ci /* 1908c2ecf20Sopenharmony_ci * when no IOMMU is available, all allocated buffers are 1918c2ecf20Sopenharmony_ci * contiguous anyway, so drop EXYNOS_BO_NONCONTIG flag 1928c2ecf20Sopenharmony_ci */ 1938c2ecf20Sopenharmony_ci flags &= ~EXYNOS_BO_NONCONTIG; 1948c2ecf20Sopenharmony_ci DRM_WARN("Non-contiguous allocation is not supported without IOMMU, falling back to contiguous buffer\n"); 1958c2ecf20Sopenharmony_ci } 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci /* set memory type and cache attribute from user side. */ 1988c2ecf20Sopenharmony_ci exynos_gem->flags = flags; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci ret = exynos_drm_alloc_buf(exynos_gem, kvmap); 2018c2ecf20Sopenharmony_ci if (ret < 0) { 2028c2ecf20Sopenharmony_ci drm_gem_object_release(&exynos_gem->base); 2038c2ecf20Sopenharmony_ci kfree(exynos_gem); 2048c2ecf20Sopenharmony_ci return ERR_PTR(ret); 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci return exynos_gem; 2088c2ecf20Sopenharmony_ci} 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ciint exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, 2118c2ecf20Sopenharmony_ci struct drm_file *file_priv) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci struct drm_exynos_gem_create *args = data; 2148c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem; 2158c2ecf20Sopenharmony_ci int ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci exynos_gem = exynos_drm_gem_create(dev, args->flags, args->size, false); 2188c2ecf20Sopenharmony_ci if (IS_ERR(exynos_gem)) 2198c2ecf20Sopenharmony_ci return PTR_ERR(exynos_gem); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, 2228c2ecf20Sopenharmony_ci &args->handle); 2238c2ecf20Sopenharmony_ci if (ret) { 2248c2ecf20Sopenharmony_ci exynos_drm_gem_destroy(exynos_gem); 2258c2ecf20Sopenharmony_ci return ret; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci return 0; 2298c2ecf20Sopenharmony_ci} 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ciint exynos_drm_gem_map_ioctl(struct drm_device *dev, void *data, 2328c2ecf20Sopenharmony_ci struct drm_file *file_priv) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci struct drm_exynos_gem_map *args = data; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci return drm_gem_dumb_map_offset(file_priv, dev, args->handle, 2378c2ecf20Sopenharmony_ci &args->offset); 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistruct exynos_drm_gem *exynos_drm_gem_get(struct drm_file *filp, 2418c2ecf20Sopenharmony_ci unsigned int gem_handle) 2428c2ecf20Sopenharmony_ci{ 2438c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci obj = drm_gem_object_lookup(filp, gem_handle); 2468c2ecf20Sopenharmony_ci if (!obj) 2478c2ecf20Sopenharmony_ci return NULL; 2488c2ecf20Sopenharmony_ci return to_exynos_gem(obj); 2498c2ecf20Sopenharmony_ci} 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_cistatic int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem, 2528c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci struct drm_device *drm_dev = exynos_gem->base.dev; 2558c2ecf20Sopenharmony_ci unsigned long vm_size; 2568c2ecf20Sopenharmony_ci int ret; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci vma->vm_flags &= ~VM_PFNMAP; 2598c2ecf20Sopenharmony_ci vma->vm_pgoff = 0; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci vm_size = vma->vm_end - vma->vm_start; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci /* check if user-requested size is valid. */ 2648c2ecf20Sopenharmony_ci if (vm_size > exynos_gem->size) 2658c2ecf20Sopenharmony_ci return -EINVAL; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, exynos_gem->cookie, 2688c2ecf20Sopenharmony_ci exynos_gem->dma_addr, exynos_gem->size, 2698c2ecf20Sopenharmony_ci exynos_gem->dma_attrs); 2708c2ecf20Sopenharmony_ci if (ret < 0) { 2718c2ecf20Sopenharmony_ci DRM_ERROR("failed to mmap.\n"); 2728c2ecf20Sopenharmony_ci return ret; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci return 0; 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ciint exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, 2798c2ecf20Sopenharmony_ci struct drm_file *file_priv) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem; 2828c2ecf20Sopenharmony_ci struct drm_exynos_gem_info *args = data; 2838c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci obj = drm_gem_object_lookup(file_priv, args->handle); 2868c2ecf20Sopenharmony_ci if (!obj) { 2878c2ecf20Sopenharmony_ci DRM_DEV_ERROR(dev->dev, "failed to lookup gem object.\n"); 2888c2ecf20Sopenharmony_ci return -EINVAL; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci exynos_gem = to_exynos_gem(obj); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci args->flags = exynos_gem->flags; 2948c2ecf20Sopenharmony_ci args->size = exynos_gem->size; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci drm_gem_object_put(obj); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci} 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_civoid exynos_drm_gem_free_object(struct drm_gem_object *obj) 3028c2ecf20Sopenharmony_ci{ 3038c2ecf20Sopenharmony_ci exynos_drm_gem_destroy(to_exynos_gem(obj)); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ciint exynos_drm_gem_dumb_create(struct drm_file *file_priv, 3078c2ecf20Sopenharmony_ci struct drm_device *dev, 3088c2ecf20Sopenharmony_ci struct drm_mode_create_dumb *args) 3098c2ecf20Sopenharmony_ci{ 3108c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem; 3118c2ecf20Sopenharmony_ci unsigned int flags; 3128c2ecf20Sopenharmony_ci int ret; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * allocate memory to be used for framebuffer. 3168c2ecf20Sopenharmony_ci * - this callback would be called by user application 3178c2ecf20Sopenharmony_ci * with DRM_IOCTL_MODE_CREATE_DUMB command. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci args->pitch = args->width * ((args->bpp + 7) / 8); 3218c2ecf20Sopenharmony_ci args->size = args->pitch * args->height; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (is_drm_iommu_supported(dev)) 3248c2ecf20Sopenharmony_ci flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC; 3258c2ecf20Sopenharmony_ci else 3268c2ecf20Sopenharmony_ci flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci exynos_gem = exynos_drm_gem_create(dev, flags, args->size, false); 3298c2ecf20Sopenharmony_ci if (IS_ERR(exynos_gem)) { 3308c2ecf20Sopenharmony_ci dev_warn(dev->dev, "FB allocation failed.\n"); 3318c2ecf20Sopenharmony_ci return PTR_ERR(exynos_gem); 3328c2ecf20Sopenharmony_ci } 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, 3358c2ecf20Sopenharmony_ci &args->handle); 3368c2ecf20Sopenharmony_ci if (ret) { 3378c2ecf20Sopenharmony_ci exynos_drm_gem_destroy(exynos_gem); 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci return 0; 3428c2ecf20Sopenharmony_ci} 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_cistatic int exynos_drm_gem_mmap_obj(struct drm_gem_object *obj, 3458c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); 3488c2ecf20Sopenharmony_ci int ret; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci DRM_DEV_DEBUG_KMS(to_dma_dev(obj->dev), "flags = 0x%x\n", 3518c2ecf20Sopenharmony_ci exynos_gem->flags); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci /* non-cachable as default. */ 3548c2ecf20Sopenharmony_ci if (exynos_gem->flags & EXYNOS_BO_CACHABLE) 3558c2ecf20Sopenharmony_ci vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); 3568c2ecf20Sopenharmony_ci else if (exynos_gem->flags & EXYNOS_BO_WC) 3578c2ecf20Sopenharmony_ci vma->vm_page_prot = 3588c2ecf20Sopenharmony_ci pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci vma->vm_page_prot = 3618c2ecf20Sopenharmony_ci pgprot_noncached(vm_get_page_prot(vma->vm_flags)); 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci ret = exynos_drm_gem_mmap_buffer(exynos_gem, vma); 3648c2ecf20Sopenharmony_ci if (ret) 3658c2ecf20Sopenharmony_ci goto err_close_vm; 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci return ret; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_cierr_close_vm: 3708c2ecf20Sopenharmony_ci drm_gem_vm_close(vma); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return ret; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ciint exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci struct drm_gem_object *obj; 3788c2ecf20Sopenharmony_ci int ret; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci /* set vm_area_struct. */ 3818c2ecf20Sopenharmony_ci ret = drm_gem_mmap(filp, vma); 3828c2ecf20Sopenharmony_ci if (ret < 0) { 3838c2ecf20Sopenharmony_ci DRM_ERROR("failed to mmap.\n"); 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci obj = vma->vm_private_data; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (obj->import_attach) 3908c2ecf20Sopenharmony_ci return dma_buf_mmap(obj->dma_buf, vma, 0); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci return exynos_drm_gem_mmap_obj(obj, vma); 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci/* low-level interface prime helpers */ 3968c2ecf20Sopenharmony_cistruct drm_gem_object *exynos_drm_gem_prime_import(struct drm_device *dev, 3978c2ecf20Sopenharmony_ci struct dma_buf *dma_buf) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci return drm_gem_prime_import_dev(dev, dma_buf, to_dma_dev(dev)); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistruct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); 4058c2ecf20Sopenharmony_ci struct drm_device *drm_dev = obj->dev; 4068c2ecf20Sopenharmony_ci struct sg_table *sgt; 4078c2ecf20Sopenharmony_ci int ret; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci sgt = kzalloc(sizeof(*sgt), GFP_KERNEL); 4108c2ecf20Sopenharmony_ci if (!sgt) 4118c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci ret = dma_get_sgtable_attrs(to_dma_dev(drm_dev), sgt, exynos_gem->cookie, 4148c2ecf20Sopenharmony_ci exynos_gem->dma_addr, exynos_gem->size, 4158c2ecf20Sopenharmony_ci exynos_gem->dma_attrs); 4168c2ecf20Sopenharmony_ci if (ret) { 4178c2ecf20Sopenharmony_ci DRM_ERROR("failed to get sgtable, %d\n", ret); 4188c2ecf20Sopenharmony_ci kfree(sgt); 4198c2ecf20Sopenharmony_ci return ERR_PTR(ret); 4208c2ecf20Sopenharmony_ci } 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci return sgt; 4238c2ecf20Sopenharmony_ci} 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_cistruct drm_gem_object * 4268c2ecf20Sopenharmony_ciexynos_drm_gem_prime_import_sg_table(struct drm_device *dev, 4278c2ecf20Sopenharmony_ci struct dma_buf_attachment *attach, 4288c2ecf20Sopenharmony_ci struct sg_table *sgt) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct exynos_drm_gem *exynos_gem; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* check if the entries in the sg_table are contiguous */ 4338c2ecf20Sopenharmony_ci if (drm_prime_get_contiguous_size(sgt) < attach->dmabuf->size) { 4348c2ecf20Sopenharmony_ci DRM_ERROR("buffer chunks must be mapped contiguously"); 4358c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size); 4398c2ecf20Sopenharmony_ci if (IS_ERR(exynos_gem)) 4408c2ecf20Sopenharmony_ci return ERR_CAST(exynos_gem); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci /* 4438c2ecf20Sopenharmony_ci * Buffer has been mapped as contiguous into DMA address space, 4448c2ecf20Sopenharmony_ci * but if there is IOMMU, it can be either CONTIG or NONCONTIG. 4458c2ecf20Sopenharmony_ci * We assume a simplified logic below: 4468c2ecf20Sopenharmony_ci */ 4478c2ecf20Sopenharmony_ci if (is_drm_iommu_supported(dev)) 4488c2ecf20Sopenharmony_ci exynos_gem->flags |= EXYNOS_BO_NONCONTIG; 4498c2ecf20Sopenharmony_ci else 4508c2ecf20Sopenharmony_ci exynos_gem->flags |= EXYNOS_BO_CONTIG; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci exynos_gem->dma_addr = sg_dma_address(sgt->sgl); 4538c2ecf20Sopenharmony_ci exynos_gem->sgt = sgt; 4548c2ecf20Sopenharmony_ci return &exynos_gem->base; 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_civoid *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci return NULL; 4608c2ecf20Sopenharmony_ci} 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_civoid exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) 4638c2ecf20Sopenharmony_ci{ 4648c2ecf20Sopenharmony_ci /* Nothing to do */ 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ciint exynos_drm_gem_prime_mmap(struct drm_gem_object *obj, 4688c2ecf20Sopenharmony_ci struct vm_area_struct *vma) 4698c2ecf20Sopenharmony_ci{ 4708c2ecf20Sopenharmony_ci int ret; 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_ci ret = drm_gem_mmap_obj(obj, obj->size, vma); 4738c2ecf20Sopenharmony_ci if (ret < 0) 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci return exynos_drm_gem_mmap_obj(obj, vma); 4778c2ecf20Sopenharmony_ci} 478