18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
48c2ecf20Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com>
58c2ecf20Sopenharmony_ci */
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci#include <linux/dma-buf.h>
88c2ecf20Sopenharmony_ci#include <linux/iommu.h>
98c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <drm/drm.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_gem.h>
138c2ecf20Sopenharmony_ci#include <drm/drm_prime.h>
148c2ecf20Sopenharmony_ci#include <drm/drm_vma_manager.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "rockchip_drm_drv.h"
178c2ecf20Sopenharmony_ci#include "rockchip_drm_gem.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	struct drm_device *drm = rk_obj->base.dev;
228c2ecf20Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
238c2ecf20Sopenharmony_ci	int prot = IOMMU_READ | IOMMU_WRITE;
248c2ecf20Sopenharmony_ci	ssize_t ret;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	mutex_lock(&private->mm_lock);
278c2ecf20Sopenharmony_ci	ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm,
288c2ecf20Sopenharmony_ci					 rk_obj->base.size, PAGE_SIZE,
298c2ecf20Sopenharmony_ci					 0, 0);
308c2ecf20Sopenharmony_ci	mutex_unlock(&private->mm_lock);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	if (ret < 0) {
338c2ecf20Sopenharmony_ci		DRM_ERROR("out of I/O virtual memory: %zd\n", ret);
348c2ecf20Sopenharmony_ci		return ret;
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	rk_obj->dma_addr = rk_obj->mm.start;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	ret = iommu_map_sgtable(private->domain, rk_obj->dma_addr, rk_obj->sgt,
408c2ecf20Sopenharmony_ci				prot);
418c2ecf20Sopenharmony_ci	if (ret < (ssize_t)rk_obj->base.size) {
428c2ecf20Sopenharmony_ci		DRM_ERROR("failed to map buffer: size=%zd request_size=%zd\n",
438c2ecf20Sopenharmony_ci			  ret, rk_obj->base.size);
448c2ecf20Sopenharmony_ci		ret = -ENOMEM;
458c2ecf20Sopenharmony_ci		goto err_remove_node;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	rk_obj->size = ret;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	return 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cierr_remove_node:
538c2ecf20Sopenharmony_ci	mutex_lock(&private->mm_lock);
548c2ecf20Sopenharmony_ci	drm_mm_remove_node(&rk_obj->mm);
558c2ecf20Sopenharmony_ci	mutex_unlock(&private->mm_lock);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return ret;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic int rockchip_gem_iommu_unmap(struct rockchip_gem_object *rk_obj)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct drm_device *drm = rk_obj->base.dev;
638c2ecf20Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	iommu_unmap(private->domain, rk_obj->dma_addr, rk_obj->size);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	mutex_lock(&private->mm_lock);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	drm_mm_remove_node(&rk_obj->mm);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	mutex_unlock(&private->mm_lock);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	return 0;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct drm_device *drm = rk_obj->base.dev;
798c2ecf20Sopenharmony_ci	int ret, i;
808c2ecf20Sopenharmony_ci	struct scatterlist *s;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	rk_obj->pages = drm_gem_get_pages(&rk_obj->base);
838c2ecf20Sopenharmony_ci	if (IS_ERR(rk_obj->pages))
848c2ecf20Sopenharmony_ci		return PTR_ERR(rk_obj->pages);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->base.dev,
898c2ecf20Sopenharmony_ci					    rk_obj->pages, rk_obj->num_pages);
908c2ecf20Sopenharmony_ci	if (IS_ERR(rk_obj->sgt)) {
918c2ecf20Sopenharmony_ci		ret = PTR_ERR(rk_obj->sgt);
928c2ecf20Sopenharmony_ci		goto err_put_pages;
938c2ecf20Sopenharmony_ci	}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	/*
968c2ecf20Sopenharmony_ci	 * Fake up the SG table so that dma_sync_sg_for_device() can be used
978c2ecf20Sopenharmony_ci	 * to flush the pages associated with it.
988c2ecf20Sopenharmony_ci	 *
998c2ecf20Sopenharmony_ci	 * TODO: Replace this by drm_clflush_sg() once it can be implemented
1008c2ecf20Sopenharmony_ci	 * without relying on symbols that are not exported.
1018c2ecf20Sopenharmony_ci	 */
1028c2ecf20Sopenharmony_ci	for_each_sgtable_sg(rk_obj->sgt, s, i)
1038c2ecf20Sopenharmony_ci		sg_dma_address(s) = sg_phys(s);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	dma_sync_sgtable_for_device(drm->dev, rk_obj->sgt, DMA_TO_DEVICE);
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	return 0;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cierr_put_pages:
1108c2ecf20Sopenharmony_ci	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false);
1118c2ecf20Sopenharmony_ci	return ret;
1128c2ecf20Sopenharmony_ci}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_cistatic void rockchip_gem_put_pages(struct rockchip_gem_object *rk_obj)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	sg_free_table(rk_obj->sgt);
1178c2ecf20Sopenharmony_ci	kfree(rk_obj->sgt);
1188c2ecf20Sopenharmony_ci	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, true, true);
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_cistatic int rockchip_gem_alloc_iommu(struct rockchip_gem_object *rk_obj,
1228c2ecf20Sopenharmony_ci				    bool alloc_kmap)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	int ret;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ret = rockchip_gem_get_pages(rk_obj);
1278c2ecf20Sopenharmony_ci	if (ret < 0)
1288c2ecf20Sopenharmony_ci		return ret;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	ret = rockchip_gem_iommu_map(rk_obj);
1318c2ecf20Sopenharmony_ci	if (ret < 0)
1328c2ecf20Sopenharmony_ci		goto err_free;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (alloc_kmap) {
1358c2ecf20Sopenharmony_ci		rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
1368c2ecf20Sopenharmony_ci				      pgprot_writecombine(PAGE_KERNEL));
1378c2ecf20Sopenharmony_ci		if (!rk_obj->kvaddr) {
1388c2ecf20Sopenharmony_ci			DRM_ERROR("failed to vmap() buffer\n");
1398c2ecf20Sopenharmony_ci			ret = -ENOMEM;
1408c2ecf20Sopenharmony_ci			goto err_unmap;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	return 0;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cierr_unmap:
1478c2ecf20Sopenharmony_ci	rockchip_gem_iommu_unmap(rk_obj);
1488c2ecf20Sopenharmony_cierr_free:
1498c2ecf20Sopenharmony_ci	rockchip_gem_put_pages(rk_obj);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return ret;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
1558c2ecf20Sopenharmony_ci				  bool alloc_kmap)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct drm_gem_object *obj = &rk_obj->base;
1588c2ecf20Sopenharmony_ci	struct drm_device *drm = obj->dev;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	if (!alloc_kmap)
1638c2ecf20Sopenharmony_ci		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
1668c2ecf20Sopenharmony_ci					 &rk_obj->dma_addr, GFP_KERNEL,
1678c2ecf20Sopenharmony_ci					 rk_obj->dma_attrs);
1688c2ecf20Sopenharmony_ci	if (!rk_obj->kvaddr) {
1698c2ecf20Sopenharmony_ci		DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
1708c2ecf20Sopenharmony_ci		return -ENOMEM;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return 0;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
1778c2ecf20Sopenharmony_ci				  bool alloc_kmap)
1788c2ecf20Sopenharmony_ci{
1798c2ecf20Sopenharmony_ci	struct drm_gem_object *obj = &rk_obj->base;
1808c2ecf20Sopenharmony_ci	struct drm_device *drm = obj->dev;
1818c2ecf20Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (private->domain)
1848c2ecf20Sopenharmony_ci		return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
1858c2ecf20Sopenharmony_ci	else
1868c2ecf20Sopenharmony_ci		return rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	vunmap(rk_obj->kvaddr);
1928c2ecf20Sopenharmony_ci	rockchip_gem_iommu_unmap(rk_obj);
1938c2ecf20Sopenharmony_ci	rockchip_gem_put_pages(rk_obj);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct drm_gem_object *obj = &rk_obj->base;
1998c2ecf20Sopenharmony_ci	struct drm_device *drm = obj->dev;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
2028c2ecf20Sopenharmony_ci		       rk_obj->dma_attrs);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_cistatic void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	if (rk_obj->pages)
2088c2ecf20Sopenharmony_ci		rockchip_gem_free_iommu(rk_obj);
2098c2ecf20Sopenharmony_ci	else
2108c2ecf20Sopenharmony_ci		rockchip_gem_free_dma(rk_obj);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int rockchip_drm_gem_object_mmap_iommu(struct drm_gem_object *obj,
2148c2ecf20Sopenharmony_ci					      struct vm_area_struct *vma)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
2178c2ecf20Sopenharmony_ci	unsigned int count = obj->size >> PAGE_SHIFT;
2188c2ecf20Sopenharmony_ci	unsigned long user_count = vma_pages(vma);
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	if (user_count == 0)
2218c2ecf20Sopenharmony_ci		return -ENXIO;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	return vm_map_pages(vma, rk_obj->pages, count);
2248c2ecf20Sopenharmony_ci}
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_cistatic int rockchip_drm_gem_object_mmap_dma(struct drm_gem_object *obj,
2278c2ecf20Sopenharmony_ci					    struct vm_area_struct *vma)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
2308c2ecf20Sopenharmony_ci	struct drm_device *drm = obj->dev;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
2338c2ecf20Sopenharmony_ci			      obj->size, rk_obj->dma_attrs);
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
2378c2ecf20Sopenharmony_ci					struct vm_area_struct *vma)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	int ret;
2408c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	/*
2438c2ecf20Sopenharmony_ci	 * We allocated a struct page table for rk_obj, so clear
2448c2ecf20Sopenharmony_ci	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
2458c2ecf20Sopenharmony_ci	 */
2468c2ecf20Sopenharmony_ci	vma->vm_flags &= ~VM_PFNMAP;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	if (rk_obj->pages)
2498c2ecf20Sopenharmony_ci		ret = rockchip_drm_gem_object_mmap_iommu(obj, vma);
2508c2ecf20Sopenharmony_ci	else
2518c2ecf20Sopenharmony_ci		ret = rockchip_drm_gem_object_mmap_dma(obj, vma);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	return ret;
2548c2ecf20Sopenharmony_ci}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ciint rockchip_gem_mmap_buf(struct drm_gem_object *obj,
2578c2ecf20Sopenharmony_ci			  struct vm_area_struct *vma)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	int ret;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	ret = drm_gem_mmap_obj(obj, obj->size, vma);
2628c2ecf20Sopenharmony_ci	if (ret)
2638c2ecf20Sopenharmony_ci		return ret;
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	return rockchip_drm_gem_object_mmap(obj, vma);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci/* drm driver mmap file operations */
2698c2ecf20Sopenharmony_ciint rockchip_gem_mmap(struct file *filp, struct vm_area_struct *vma)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	struct drm_gem_object *obj;
2728c2ecf20Sopenharmony_ci	int ret;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	ret = drm_gem_mmap(filp, vma);
2758c2ecf20Sopenharmony_ci	if (ret)
2768c2ecf20Sopenharmony_ci		return ret;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	/*
2798c2ecf20Sopenharmony_ci	 * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the
2808c2ecf20Sopenharmony_ci	 * whole buffer from the start.
2818c2ecf20Sopenharmony_ci	 */
2828c2ecf20Sopenharmony_ci	vma->vm_pgoff = 0;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	obj = vma->vm_private_data;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	return rockchip_drm_gem_object_mmap(obj, vma);
2878c2ecf20Sopenharmony_ci}
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci	drm_gem_object_release(&rk_obj->base);
2928c2ecf20Sopenharmony_ci	kfree(rk_obj);
2938c2ecf20Sopenharmony_ci}
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_cistatic struct rockchip_gem_object *
2968c2ecf20Sopenharmony_ci	rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
2998c2ecf20Sopenharmony_ci	struct drm_gem_object *obj;
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	size = round_up(size, PAGE_SIZE);
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
3048c2ecf20Sopenharmony_ci	if (!rk_obj)
3058c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	obj = &rk_obj->base;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	drm_gem_object_init(drm, obj, size);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	return rk_obj;
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistruct rockchip_gem_object *
3158c2ecf20Sopenharmony_cirockchip_gem_create_object(struct drm_device *drm, unsigned int size,
3168c2ecf20Sopenharmony_ci			   bool alloc_kmap)
3178c2ecf20Sopenharmony_ci{
3188c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
3198c2ecf20Sopenharmony_ci	int ret;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	rk_obj = rockchip_gem_alloc_object(drm, size);
3228c2ecf20Sopenharmony_ci	if (IS_ERR(rk_obj))
3238c2ecf20Sopenharmony_ci		return rk_obj;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
3268c2ecf20Sopenharmony_ci	if (ret)
3278c2ecf20Sopenharmony_ci		goto err_free_rk_obj;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return rk_obj;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_cierr_free_rk_obj:
3328c2ecf20Sopenharmony_ci	rockchip_gem_release_object(rk_obj);
3338c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/*
3378c2ecf20Sopenharmony_ci * rockchip_gem_free_object - (struct drm_driver)->gem_free_object_unlocked
3388c2ecf20Sopenharmony_ci * callback function
3398c2ecf20Sopenharmony_ci */
3408c2ecf20Sopenharmony_civoid rockchip_gem_free_object(struct drm_gem_object *obj)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct drm_device *drm = obj->dev;
3438c2ecf20Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
3448c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (obj->import_attach) {
3478c2ecf20Sopenharmony_ci		if (private->domain) {
3488c2ecf20Sopenharmony_ci			rockchip_gem_iommu_unmap(rk_obj);
3498c2ecf20Sopenharmony_ci		} else {
3508c2ecf20Sopenharmony_ci			dma_unmap_sgtable(drm->dev, rk_obj->sgt,
3518c2ecf20Sopenharmony_ci					  DMA_BIDIRECTIONAL, 0);
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		drm_prime_gem_destroy(obj, rk_obj->sgt);
3548c2ecf20Sopenharmony_ci	} else {
3558c2ecf20Sopenharmony_ci		rockchip_gem_free_buf(rk_obj);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	rockchip_gem_release_object(rk_obj);
3598c2ecf20Sopenharmony_ci}
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci/*
3628c2ecf20Sopenharmony_ci * rockchip_gem_create_with_handle - allocate an object with the given
3638c2ecf20Sopenharmony_ci * size and create a gem handle on it
3648c2ecf20Sopenharmony_ci *
3658c2ecf20Sopenharmony_ci * returns a struct rockchip_gem_object* on success or ERR_PTR values
3668c2ecf20Sopenharmony_ci * on failure.
3678c2ecf20Sopenharmony_ci */
3688c2ecf20Sopenharmony_cistatic struct rockchip_gem_object *
3698c2ecf20Sopenharmony_cirockchip_gem_create_with_handle(struct drm_file *file_priv,
3708c2ecf20Sopenharmony_ci				struct drm_device *drm, unsigned int size,
3718c2ecf20Sopenharmony_ci				unsigned int *handle)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
3748c2ecf20Sopenharmony_ci	struct drm_gem_object *obj;
3758c2ecf20Sopenharmony_ci	int ret;
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci	rk_obj = rockchip_gem_create_object(drm, size, false);
3788c2ecf20Sopenharmony_ci	if (IS_ERR(rk_obj))
3798c2ecf20Sopenharmony_ci		return ERR_CAST(rk_obj);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	obj = &rk_obj->base;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	/*
3848c2ecf20Sopenharmony_ci	 * allocate a id of idr table where the obj is registered
3858c2ecf20Sopenharmony_ci	 * and handle has the id what user can see.
3868c2ecf20Sopenharmony_ci	 */
3878c2ecf20Sopenharmony_ci	ret = drm_gem_handle_create(file_priv, obj, handle);
3888c2ecf20Sopenharmony_ci	if (ret)
3898c2ecf20Sopenharmony_ci		goto err_handle_create;
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	/* drop reference from allocate - handle holds it now. */
3928c2ecf20Sopenharmony_ci	drm_gem_object_put(obj);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	return rk_obj;
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_cierr_handle_create:
3978c2ecf20Sopenharmony_ci	rockchip_gem_free_object(obj);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/*
4038c2ecf20Sopenharmony_ci * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
4048c2ecf20Sopenharmony_ci * function
4058c2ecf20Sopenharmony_ci *
4068c2ecf20Sopenharmony_ci * This aligns the pitch and size arguments to the minimum required. wrap
4078c2ecf20Sopenharmony_ci * this into your own function if you need bigger alignment.
4088c2ecf20Sopenharmony_ci */
4098c2ecf20Sopenharmony_ciint rockchip_gem_dumb_create(struct drm_file *file_priv,
4108c2ecf20Sopenharmony_ci			     struct drm_device *dev,
4118c2ecf20Sopenharmony_ci			     struct drm_mode_create_dumb *args)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
4148c2ecf20Sopenharmony_ci	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	/*
4178c2ecf20Sopenharmony_ci	 * align to 64 bytes since Mali requires it.
4188c2ecf20Sopenharmony_ci	 */
4198c2ecf20Sopenharmony_ci	args->pitch = ALIGN(min_pitch, 64);
4208c2ecf20Sopenharmony_ci	args->size = args->pitch * args->height;
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
4238c2ecf20Sopenharmony_ci						 &args->handle);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(rk_obj);
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci/*
4298c2ecf20Sopenharmony_ci * Allocate a sg_table for this GEM object.
4308c2ecf20Sopenharmony_ci * Note: Both the table's contents, and the sg_table itself must be freed by
4318c2ecf20Sopenharmony_ci *       the caller.
4328c2ecf20Sopenharmony_ci * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
4338c2ecf20Sopenharmony_ci */
4348c2ecf20Sopenharmony_cistruct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
4358c2ecf20Sopenharmony_ci{
4368c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
4378c2ecf20Sopenharmony_ci	struct drm_device *drm = obj->dev;
4388c2ecf20Sopenharmony_ci	struct sg_table *sgt;
4398c2ecf20Sopenharmony_ci	int ret;
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci	if (rk_obj->pages)
4428c2ecf20Sopenharmony_ci		return drm_prime_pages_to_sg(obj->dev, rk_obj->pages, rk_obj->num_pages);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
4458c2ecf20Sopenharmony_ci	if (!sgt)
4468c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
4498c2ecf20Sopenharmony_ci				    rk_obj->dma_addr, obj->size,
4508c2ecf20Sopenharmony_ci				    rk_obj->dma_attrs);
4518c2ecf20Sopenharmony_ci	if (ret) {
4528c2ecf20Sopenharmony_ci		DRM_ERROR("failed to allocate sgt, %d\n", ret);
4538c2ecf20Sopenharmony_ci		kfree(sgt);
4548c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
4558c2ecf20Sopenharmony_ci	}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_ci	return sgt;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic int
4618c2ecf20Sopenharmony_cirockchip_gem_iommu_map_sg(struct drm_device *drm,
4628c2ecf20Sopenharmony_ci			  struct dma_buf_attachment *attach,
4638c2ecf20Sopenharmony_ci			  struct sg_table *sg,
4648c2ecf20Sopenharmony_ci			  struct rockchip_gem_object *rk_obj)
4658c2ecf20Sopenharmony_ci{
4668c2ecf20Sopenharmony_ci	rk_obj->sgt = sg;
4678c2ecf20Sopenharmony_ci	return rockchip_gem_iommu_map(rk_obj);
4688c2ecf20Sopenharmony_ci}
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_cistatic int
4718c2ecf20Sopenharmony_cirockchip_gem_dma_map_sg(struct drm_device *drm,
4728c2ecf20Sopenharmony_ci			struct dma_buf_attachment *attach,
4738c2ecf20Sopenharmony_ci			struct sg_table *sg,
4748c2ecf20Sopenharmony_ci			struct rockchip_gem_object *rk_obj)
4758c2ecf20Sopenharmony_ci{
4768c2ecf20Sopenharmony_ci	int err = dma_map_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
4778c2ecf20Sopenharmony_ci	if (err)
4788c2ecf20Sopenharmony_ci		return err;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (drm_prime_get_contiguous_size(sg) < attach->dmabuf->size) {
4818c2ecf20Sopenharmony_ci		DRM_ERROR("failed to map sg_table to contiguous linear address.\n");
4828c2ecf20Sopenharmony_ci		dma_unmap_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
4838c2ecf20Sopenharmony_ci		return -EINVAL;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	rk_obj->dma_addr = sg_dma_address(sg->sgl);
4878c2ecf20Sopenharmony_ci	rk_obj->sgt = sg;
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistruct drm_gem_object *
4928c2ecf20Sopenharmony_cirockchip_gem_prime_import_sg_table(struct drm_device *drm,
4938c2ecf20Sopenharmony_ci				   struct dma_buf_attachment *attach,
4948c2ecf20Sopenharmony_ci				   struct sg_table *sg)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
4978c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
4988c2ecf20Sopenharmony_ci	int ret;
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	rk_obj = rockchip_gem_alloc_object(drm, attach->dmabuf->size);
5018c2ecf20Sopenharmony_ci	if (IS_ERR(rk_obj))
5028c2ecf20Sopenharmony_ci		return ERR_CAST(rk_obj);
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (private->domain)
5058c2ecf20Sopenharmony_ci		ret = rockchip_gem_iommu_map_sg(drm, attach, sg, rk_obj);
5068c2ecf20Sopenharmony_ci	else
5078c2ecf20Sopenharmony_ci		ret = rockchip_gem_dma_map_sg(drm, attach, sg, rk_obj);
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	if (ret < 0) {
5108c2ecf20Sopenharmony_ci		DRM_ERROR("failed to import sg table: %d\n", ret);
5118c2ecf20Sopenharmony_ci		goto err_free_rk_obj;
5128c2ecf20Sopenharmony_ci	}
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci	return &rk_obj->base;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cierr_free_rk_obj:
5178c2ecf20Sopenharmony_ci	rockchip_gem_release_object(rk_obj);
5188c2ecf20Sopenharmony_ci	return ERR_PTR(ret);
5198c2ecf20Sopenharmony_ci}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_civoid *rockchip_gem_prime_vmap(struct drm_gem_object *obj)
5228c2ecf20Sopenharmony_ci{
5238c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (rk_obj->pages)
5268c2ecf20Sopenharmony_ci		return vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
5278c2ecf20Sopenharmony_ci			    pgprot_writecombine(PAGE_KERNEL));
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	if (rk_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
5308c2ecf20Sopenharmony_ci		return NULL;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	return rk_obj->kvaddr;
5338c2ecf20Sopenharmony_ci}
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_civoid rockchip_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr)
5368c2ecf20Sopenharmony_ci{
5378c2ecf20Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (rk_obj->pages) {
5408c2ecf20Sopenharmony_ci		vunmap(vaddr);
5418c2ecf20Sopenharmony_ci		return;
5428c2ecf20Sopenharmony_ci	}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	/* Nothing to do if allocated by DMA mapping API. */
5458c2ecf20Sopenharmony_ci}
546