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