162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
462306a36Sopenharmony_ci * Author:Mark Yao <mark.yao@rock-chips.com>
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/dma-buf.h>
862306a36Sopenharmony_ci#include <linux/iommu.h>
962306a36Sopenharmony_ci#include <linux/vmalloc.h>
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <drm/drm.h>
1262306a36Sopenharmony_ci#include <drm/drm_fb_helper.h>
1362306a36Sopenharmony_ci#include <drm/drm_gem.h>
1462306a36Sopenharmony_ci#include <drm/drm_gem_dma_helper.h>
1562306a36Sopenharmony_ci#include <drm/drm_prime.h>
1662306a36Sopenharmony_ci#include <drm/drm_vma_manager.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "rockchip_drm_drv.h"
1962306a36Sopenharmony_ci#include "rockchip_drm_gem.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic int rockchip_gem_iommu_map(struct rockchip_gem_object *rk_obj)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct drm_device *drm = rk_obj->base.dev;
2462306a36Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
2562306a36Sopenharmony_ci	int prot = IOMMU_READ | IOMMU_WRITE;
2662306a36Sopenharmony_ci	ssize_t ret;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	mutex_lock(&private->mm_lock);
2962306a36Sopenharmony_ci	ret = drm_mm_insert_node_generic(&private->mm, &rk_obj->mm,
3062306a36Sopenharmony_ci					 rk_obj->base.size, PAGE_SIZE,
3162306a36Sopenharmony_ci					 0, 0);
3262306a36Sopenharmony_ci	mutex_unlock(&private->mm_lock);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (ret < 0) {
3562306a36Sopenharmony_ci		DRM_ERROR("out of I/O virtual memory: %zd\n", ret);
3662306a36Sopenharmony_ci		return ret;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	rk_obj->dma_addr = rk_obj->mm.start;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	ret = iommu_map_sgtable(private->domain, rk_obj->dma_addr, rk_obj->sgt,
4262306a36Sopenharmony_ci				prot);
4362306a36Sopenharmony_ci	if (ret < (ssize_t)rk_obj->base.size) {
4462306a36Sopenharmony_ci		DRM_ERROR("failed to map buffer: size=%zd request_size=%zd\n",
4562306a36Sopenharmony_ci			  ret, rk_obj->base.size);
4662306a36Sopenharmony_ci		ret = -ENOMEM;
4762306a36Sopenharmony_ci		goto err_remove_node;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	rk_obj->size = ret;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	return 0;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cierr_remove_node:
5562306a36Sopenharmony_ci	mutex_lock(&private->mm_lock);
5662306a36Sopenharmony_ci	drm_mm_remove_node(&rk_obj->mm);
5762306a36Sopenharmony_ci	mutex_unlock(&private->mm_lock);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	return ret;
6062306a36Sopenharmony_ci}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic int rockchip_gem_iommu_unmap(struct rockchip_gem_object *rk_obj)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct drm_device *drm = rk_obj->base.dev;
6562306a36Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	iommu_unmap(private->domain, rk_obj->dma_addr, rk_obj->size);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	mutex_lock(&private->mm_lock);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	drm_mm_remove_node(&rk_obj->mm);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	mutex_unlock(&private->mm_lock);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	return 0;
7662306a36Sopenharmony_ci}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_cistatic int rockchip_gem_get_pages(struct rockchip_gem_object *rk_obj)
7962306a36Sopenharmony_ci{
8062306a36Sopenharmony_ci	struct drm_device *drm = rk_obj->base.dev;
8162306a36Sopenharmony_ci	int ret, i;
8262306a36Sopenharmony_ci	struct scatterlist *s;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	rk_obj->pages = drm_gem_get_pages(&rk_obj->base);
8562306a36Sopenharmony_ci	if (IS_ERR(rk_obj->pages))
8662306a36Sopenharmony_ci		return PTR_ERR(rk_obj->pages);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	rk_obj->num_pages = rk_obj->base.size >> PAGE_SHIFT;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	rk_obj->sgt = drm_prime_pages_to_sg(rk_obj->base.dev,
9162306a36Sopenharmony_ci					    rk_obj->pages, rk_obj->num_pages);
9262306a36Sopenharmony_ci	if (IS_ERR(rk_obj->sgt)) {
9362306a36Sopenharmony_ci		ret = PTR_ERR(rk_obj->sgt);
9462306a36Sopenharmony_ci		goto err_put_pages;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	/*
9862306a36Sopenharmony_ci	 * Fake up the SG table so that dma_sync_sg_for_device() can be used
9962306a36Sopenharmony_ci	 * to flush the pages associated with it.
10062306a36Sopenharmony_ci	 *
10162306a36Sopenharmony_ci	 * TODO: Replace this by drm_clflush_sg() once it can be implemented
10262306a36Sopenharmony_ci	 * without relying on symbols that are not exported.
10362306a36Sopenharmony_ci	 */
10462306a36Sopenharmony_ci	for_each_sgtable_sg(rk_obj->sgt, s, i)
10562306a36Sopenharmony_ci		sg_dma_address(s) = sg_phys(s);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	dma_sync_sgtable_for_device(drm->dev, rk_obj->sgt, DMA_TO_DEVICE);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cierr_put_pages:
11262306a36Sopenharmony_ci	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, false, false);
11362306a36Sopenharmony_ci	return ret;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void rockchip_gem_put_pages(struct rockchip_gem_object *rk_obj)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	sg_free_table(rk_obj->sgt);
11962306a36Sopenharmony_ci	kfree(rk_obj->sgt);
12062306a36Sopenharmony_ci	drm_gem_put_pages(&rk_obj->base, rk_obj->pages, true, true);
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_cistatic int rockchip_gem_alloc_iommu(struct rockchip_gem_object *rk_obj,
12462306a36Sopenharmony_ci				    bool alloc_kmap)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int ret;
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	ret = rockchip_gem_get_pages(rk_obj);
12962306a36Sopenharmony_ci	if (ret < 0)
13062306a36Sopenharmony_ci		return ret;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	ret = rockchip_gem_iommu_map(rk_obj);
13362306a36Sopenharmony_ci	if (ret < 0)
13462306a36Sopenharmony_ci		goto err_free;
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	if (alloc_kmap) {
13762306a36Sopenharmony_ci		rk_obj->kvaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
13862306a36Sopenharmony_ci				      pgprot_writecombine(PAGE_KERNEL));
13962306a36Sopenharmony_ci		if (!rk_obj->kvaddr) {
14062306a36Sopenharmony_ci			DRM_ERROR("failed to vmap() buffer\n");
14162306a36Sopenharmony_ci			ret = -ENOMEM;
14262306a36Sopenharmony_ci			goto err_unmap;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cierr_unmap:
14962306a36Sopenharmony_ci	rockchip_gem_iommu_unmap(rk_obj);
15062306a36Sopenharmony_cierr_free:
15162306a36Sopenharmony_ci	rockchip_gem_put_pages(rk_obj);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return ret;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_cistatic int rockchip_gem_alloc_dma(struct rockchip_gem_object *rk_obj,
15762306a36Sopenharmony_ci				  bool alloc_kmap)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	struct drm_gem_object *obj = &rk_obj->base;
16062306a36Sopenharmony_ci	struct drm_device *drm = obj->dev;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	rk_obj->dma_attrs = DMA_ATTR_WRITE_COMBINE;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (!alloc_kmap)
16562306a36Sopenharmony_ci		rk_obj->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	rk_obj->kvaddr = dma_alloc_attrs(drm->dev, obj->size,
16862306a36Sopenharmony_ci					 &rk_obj->dma_addr, GFP_KERNEL,
16962306a36Sopenharmony_ci					 rk_obj->dma_attrs);
17062306a36Sopenharmony_ci	if (!rk_obj->kvaddr) {
17162306a36Sopenharmony_ci		DRM_ERROR("failed to allocate %zu byte dma buffer", obj->size);
17262306a36Sopenharmony_ci		return -ENOMEM;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cistatic int rockchip_gem_alloc_buf(struct rockchip_gem_object *rk_obj,
17962306a36Sopenharmony_ci				  bool alloc_kmap)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	struct drm_gem_object *obj = &rk_obj->base;
18262306a36Sopenharmony_ci	struct drm_device *drm = obj->dev;
18362306a36Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	if (private->domain)
18662306a36Sopenharmony_ci		return rockchip_gem_alloc_iommu(rk_obj, alloc_kmap);
18762306a36Sopenharmony_ci	else
18862306a36Sopenharmony_ci		return rockchip_gem_alloc_dma(rk_obj, alloc_kmap);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic void rockchip_gem_free_iommu(struct rockchip_gem_object *rk_obj)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	vunmap(rk_obj->kvaddr);
19462306a36Sopenharmony_ci	rockchip_gem_iommu_unmap(rk_obj);
19562306a36Sopenharmony_ci	rockchip_gem_put_pages(rk_obj);
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cistatic void rockchip_gem_free_dma(struct rockchip_gem_object *rk_obj)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	struct drm_gem_object *obj = &rk_obj->base;
20162306a36Sopenharmony_ci	struct drm_device *drm = obj->dev;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	dma_free_attrs(drm->dev, obj->size, rk_obj->kvaddr, rk_obj->dma_addr,
20462306a36Sopenharmony_ci		       rk_obj->dma_attrs);
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void rockchip_gem_free_buf(struct rockchip_gem_object *rk_obj)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	if (rk_obj->pages)
21062306a36Sopenharmony_ci		rockchip_gem_free_iommu(rk_obj);
21162306a36Sopenharmony_ci	else
21262306a36Sopenharmony_ci		rockchip_gem_free_dma(rk_obj);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_cistatic int rockchip_drm_gem_object_mmap_iommu(struct drm_gem_object *obj,
21662306a36Sopenharmony_ci					      struct vm_area_struct *vma)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
21962306a36Sopenharmony_ci	unsigned int count = obj->size >> PAGE_SHIFT;
22062306a36Sopenharmony_ci	unsigned long user_count = vma_pages(vma);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	if (user_count == 0)
22362306a36Sopenharmony_ci		return -ENXIO;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return vm_map_pages(vma, rk_obj->pages, count);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int rockchip_drm_gem_object_mmap_dma(struct drm_gem_object *obj,
22962306a36Sopenharmony_ci					    struct vm_area_struct *vma)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
23262306a36Sopenharmony_ci	struct drm_device *drm = obj->dev;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	return dma_mmap_attrs(drm->dev, vma, rk_obj->kvaddr, rk_obj->dma_addr,
23562306a36Sopenharmony_ci			      obj->size, rk_obj->dma_attrs);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic int rockchip_drm_gem_object_mmap(struct drm_gem_object *obj,
23962306a36Sopenharmony_ci					struct vm_area_struct *vma)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	int ret;
24262306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	/*
24562306a36Sopenharmony_ci	 * Set vm_pgoff (used as a fake buffer offset by DRM) to 0 and map the
24662306a36Sopenharmony_ci	 * whole buffer from the start.
24762306a36Sopenharmony_ci	 */
24862306a36Sopenharmony_ci	vma->vm_pgoff = 0;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	/*
25162306a36Sopenharmony_ci	 * We allocated a struct page table for rk_obj, so clear
25262306a36Sopenharmony_ci	 * VM_PFNMAP flag that was set by drm_gem_mmap_obj()/drm_gem_mmap().
25362306a36Sopenharmony_ci	 */
25462306a36Sopenharmony_ci	vm_flags_mod(vma, VM_IO | VM_DONTEXPAND | VM_DONTDUMP, VM_PFNMAP);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	vma->vm_page_prot = pgprot_writecombine(vm_get_page_prot(vma->vm_flags));
25762306a36Sopenharmony_ci	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (rk_obj->pages)
26062306a36Sopenharmony_ci		ret = rockchip_drm_gem_object_mmap_iommu(obj, vma);
26162306a36Sopenharmony_ci	else
26262306a36Sopenharmony_ci		ret = rockchip_drm_gem_object_mmap_dma(obj, vma);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	return ret;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic void rockchip_gem_release_object(struct rockchip_gem_object *rk_obj)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	drm_gem_object_release(&rk_obj->base);
27062306a36Sopenharmony_ci	kfree(rk_obj);
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic const struct drm_gem_object_funcs rockchip_gem_object_funcs = {
27462306a36Sopenharmony_ci	.free = rockchip_gem_free_object,
27562306a36Sopenharmony_ci	.get_sg_table = rockchip_gem_prime_get_sg_table,
27662306a36Sopenharmony_ci	.vmap = rockchip_gem_prime_vmap,
27762306a36Sopenharmony_ci	.vunmap	= rockchip_gem_prime_vunmap,
27862306a36Sopenharmony_ci	.mmap = rockchip_drm_gem_object_mmap,
27962306a36Sopenharmony_ci	.vm_ops = &drm_gem_dma_vm_ops,
28062306a36Sopenharmony_ci};
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic struct rockchip_gem_object *
28362306a36Sopenharmony_ci	rockchip_gem_alloc_object(struct drm_device *drm, unsigned int size)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
28662306a36Sopenharmony_ci	struct drm_gem_object *obj;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	size = round_up(size, PAGE_SIZE);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	rk_obj = kzalloc(sizeof(*rk_obj), GFP_KERNEL);
29162306a36Sopenharmony_ci	if (!rk_obj)
29262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	obj = &rk_obj->base;
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	obj->funcs = &rockchip_gem_object_funcs;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	drm_gem_object_init(drm, obj, size);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	return rk_obj;
30162306a36Sopenharmony_ci}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_cistruct rockchip_gem_object *
30462306a36Sopenharmony_cirockchip_gem_create_object(struct drm_device *drm, unsigned int size,
30562306a36Sopenharmony_ci			   bool alloc_kmap)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
30862306a36Sopenharmony_ci	int ret;
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	rk_obj = rockchip_gem_alloc_object(drm, size);
31162306a36Sopenharmony_ci	if (IS_ERR(rk_obj))
31262306a36Sopenharmony_ci		return rk_obj;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = rockchip_gem_alloc_buf(rk_obj, alloc_kmap);
31562306a36Sopenharmony_ci	if (ret)
31662306a36Sopenharmony_ci		goto err_free_rk_obj;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	return rk_obj;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_cierr_free_rk_obj:
32162306a36Sopenharmony_ci	rockchip_gem_release_object(rk_obj);
32262306a36Sopenharmony_ci	return ERR_PTR(ret);
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci/*
32662306a36Sopenharmony_ci * rockchip_gem_free_object - (struct drm_gem_object_funcs)->free
32762306a36Sopenharmony_ci * callback function
32862306a36Sopenharmony_ci */
32962306a36Sopenharmony_civoid rockchip_gem_free_object(struct drm_gem_object *obj)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct drm_device *drm = obj->dev;
33262306a36Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
33362306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (obj->import_attach) {
33662306a36Sopenharmony_ci		if (private->domain) {
33762306a36Sopenharmony_ci			rockchip_gem_iommu_unmap(rk_obj);
33862306a36Sopenharmony_ci		} else {
33962306a36Sopenharmony_ci			dma_unmap_sgtable(drm->dev, rk_obj->sgt,
34062306a36Sopenharmony_ci					  DMA_BIDIRECTIONAL, 0);
34162306a36Sopenharmony_ci		}
34262306a36Sopenharmony_ci		drm_prime_gem_destroy(obj, rk_obj->sgt);
34362306a36Sopenharmony_ci	} else {
34462306a36Sopenharmony_ci		rockchip_gem_free_buf(rk_obj);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	rockchip_gem_release_object(rk_obj);
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci/*
35162306a36Sopenharmony_ci * rockchip_gem_create_with_handle - allocate an object with the given
35262306a36Sopenharmony_ci * size and create a gem handle on it
35362306a36Sopenharmony_ci *
35462306a36Sopenharmony_ci * returns a struct rockchip_gem_object* on success or ERR_PTR values
35562306a36Sopenharmony_ci * on failure.
35662306a36Sopenharmony_ci */
35762306a36Sopenharmony_cistatic struct rockchip_gem_object *
35862306a36Sopenharmony_cirockchip_gem_create_with_handle(struct drm_file *file_priv,
35962306a36Sopenharmony_ci				struct drm_device *drm, unsigned int size,
36062306a36Sopenharmony_ci				unsigned int *handle)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
36362306a36Sopenharmony_ci	struct drm_gem_object *obj;
36462306a36Sopenharmony_ci	bool is_framebuffer;
36562306a36Sopenharmony_ci	int ret;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	is_framebuffer = drm->fb_helper && file_priv == drm->fb_helper->client.file;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	rk_obj = rockchip_gem_create_object(drm, size, is_framebuffer);
37062306a36Sopenharmony_ci	if (IS_ERR(rk_obj))
37162306a36Sopenharmony_ci		return ERR_CAST(rk_obj);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	obj = &rk_obj->base;
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	/*
37662306a36Sopenharmony_ci	 * allocate a id of idr table where the obj is registered
37762306a36Sopenharmony_ci	 * and handle has the id what user can see.
37862306a36Sopenharmony_ci	 */
37962306a36Sopenharmony_ci	ret = drm_gem_handle_create(file_priv, obj, handle);
38062306a36Sopenharmony_ci	if (ret)
38162306a36Sopenharmony_ci		goto err_handle_create;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	/* drop reference from allocate - handle holds it now. */
38462306a36Sopenharmony_ci	drm_gem_object_put(obj);
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	return rk_obj;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_cierr_handle_create:
38962306a36Sopenharmony_ci	rockchip_gem_free_object(obj);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	return ERR_PTR(ret);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/*
39562306a36Sopenharmony_ci * rockchip_gem_dumb_create - (struct drm_driver)->dumb_create callback
39662306a36Sopenharmony_ci * function
39762306a36Sopenharmony_ci *
39862306a36Sopenharmony_ci * This aligns the pitch and size arguments to the minimum required. wrap
39962306a36Sopenharmony_ci * this into your own function if you need bigger alignment.
40062306a36Sopenharmony_ci */
40162306a36Sopenharmony_ciint rockchip_gem_dumb_create(struct drm_file *file_priv,
40262306a36Sopenharmony_ci			     struct drm_device *dev,
40362306a36Sopenharmony_ci			     struct drm_mode_create_dumb *args)
40462306a36Sopenharmony_ci{
40562306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
40662306a36Sopenharmony_ci	int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	/*
40962306a36Sopenharmony_ci	 * align to 64 bytes since Mali requires it.
41062306a36Sopenharmony_ci	 */
41162306a36Sopenharmony_ci	args->pitch = ALIGN(min_pitch, 64);
41262306a36Sopenharmony_ci	args->size = args->pitch * args->height;
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	rk_obj = rockchip_gem_create_with_handle(file_priv, dev, args->size,
41562306a36Sopenharmony_ci						 &args->handle);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	return PTR_ERR_OR_ZERO(rk_obj);
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_ci/*
42162306a36Sopenharmony_ci * Allocate a sg_table for this GEM object.
42262306a36Sopenharmony_ci * Note: Both the table's contents, and the sg_table itself must be freed by
42362306a36Sopenharmony_ci *       the caller.
42462306a36Sopenharmony_ci * Returns a pointer to the newly allocated sg_table, or an ERR_PTR() error.
42562306a36Sopenharmony_ci */
42662306a36Sopenharmony_cistruct sg_table *rockchip_gem_prime_get_sg_table(struct drm_gem_object *obj)
42762306a36Sopenharmony_ci{
42862306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
42962306a36Sopenharmony_ci	struct drm_device *drm = obj->dev;
43062306a36Sopenharmony_ci	struct sg_table *sgt;
43162306a36Sopenharmony_ci	int ret;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (rk_obj->pages)
43462306a36Sopenharmony_ci		return drm_prime_pages_to_sg(obj->dev, rk_obj->pages, rk_obj->num_pages);
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	sgt = kzalloc(sizeof(*sgt), GFP_KERNEL);
43762306a36Sopenharmony_ci	if (!sgt)
43862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	ret = dma_get_sgtable_attrs(drm->dev, sgt, rk_obj->kvaddr,
44162306a36Sopenharmony_ci				    rk_obj->dma_addr, obj->size,
44262306a36Sopenharmony_ci				    rk_obj->dma_attrs);
44362306a36Sopenharmony_ci	if (ret) {
44462306a36Sopenharmony_ci		DRM_ERROR("failed to allocate sgt, %d\n", ret);
44562306a36Sopenharmony_ci		kfree(sgt);
44662306a36Sopenharmony_ci		return ERR_PTR(ret);
44762306a36Sopenharmony_ci	}
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	return sgt;
45062306a36Sopenharmony_ci}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_cistatic int
45362306a36Sopenharmony_cirockchip_gem_iommu_map_sg(struct drm_device *drm,
45462306a36Sopenharmony_ci			  struct dma_buf_attachment *attach,
45562306a36Sopenharmony_ci			  struct sg_table *sg,
45662306a36Sopenharmony_ci			  struct rockchip_gem_object *rk_obj)
45762306a36Sopenharmony_ci{
45862306a36Sopenharmony_ci	rk_obj->sgt = sg;
45962306a36Sopenharmony_ci	return rockchip_gem_iommu_map(rk_obj);
46062306a36Sopenharmony_ci}
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_cistatic int
46362306a36Sopenharmony_cirockchip_gem_dma_map_sg(struct drm_device *drm,
46462306a36Sopenharmony_ci			struct dma_buf_attachment *attach,
46562306a36Sopenharmony_ci			struct sg_table *sg,
46662306a36Sopenharmony_ci			struct rockchip_gem_object *rk_obj)
46762306a36Sopenharmony_ci{
46862306a36Sopenharmony_ci	int err = dma_map_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
46962306a36Sopenharmony_ci	if (err)
47062306a36Sopenharmony_ci		return err;
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci	if (drm_prime_get_contiguous_size(sg) < attach->dmabuf->size) {
47362306a36Sopenharmony_ci		DRM_ERROR("failed to map sg_table to contiguous linear address.\n");
47462306a36Sopenharmony_ci		dma_unmap_sgtable(drm->dev, sg, DMA_BIDIRECTIONAL, 0);
47562306a36Sopenharmony_ci		return -EINVAL;
47662306a36Sopenharmony_ci	}
47762306a36Sopenharmony_ci
47862306a36Sopenharmony_ci	rk_obj->dma_addr = sg_dma_address(sg->sgl);
47962306a36Sopenharmony_ci	rk_obj->sgt = sg;
48062306a36Sopenharmony_ci	return 0;
48162306a36Sopenharmony_ci}
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_cistruct drm_gem_object *
48462306a36Sopenharmony_cirockchip_gem_prime_import_sg_table(struct drm_device *drm,
48562306a36Sopenharmony_ci				   struct dma_buf_attachment *attach,
48662306a36Sopenharmony_ci				   struct sg_table *sg)
48762306a36Sopenharmony_ci{
48862306a36Sopenharmony_ci	struct rockchip_drm_private *private = drm->dev_private;
48962306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj;
49062306a36Sopenharmony_ci	int ret;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	rk_obj = rockchip_gem_alloc_object(drm, attach->dmabuf->size);
49362306a36Sopenharmony_ci	if (IS_ERR(rk_obj))
49462306a36Sopenharmony_ci		return ERR_CAST(rk_obj);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	if (private->domain)
49762306a36Sopenharmony_ci		ret = rockchip_gem_iommu_map_sg(drm, attach, sg, rk_obj);
49862306a36Sopenharmony_ci	else
49962306a36Sopenharmony_ci		ret = rockchip_gem_dma_map_sg(drm, attach, sg, rk_obj);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (ret < 0) {
50262306a36Sopenharmony_ci		DRM_ERROR("failed to import sg table: %d\n", ret);
50362306a36Sopenharmony_ci		goto err_free_rk_obj;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	return &rk_obj->base;
50762306a36Sopenharmony_ci
50862306a36Sopenharmony_cierr_free_rk_obj:
50962306a36Sopenharmony_ci	rockchip_gem_release_object(rk_obj);
51062306a36Sopenharmony_ci	return ERR_PTR(ret);
51162306a36Sopenharmony_ci}
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ciint rockchip_gem_prime_vmap(struct drm_gem_object *obj, struct iosys_map *map)
51462306a36Sopenharmony_ci{
51562306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	if (rk_obj->pages) {
51862306a36Sopenharmony_ci		void *vaddr;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci		if (rk_obj->kvaddr)
52162306a36Sopenharmony_ci			vaddr = rk_obj->kvaddr;
52262306a36Sopenharmony_ci		else
52362306a36Sopenharmony_ci			vaddr = vmap(rk_obj->pages, rk_obj->num_pages, VM_MAP,
52462306a36Sopenharmony_ci				     pgprot_writecombine(PAGE_KERNEL));
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci		if (!vaddr)
52762306a36Sopenharmony_ci			return -ENOMEM;
52862306a36Sopenharmony_ci		iosys_map_set_vaddr(map, vaddr);
52962306a36Sopenharmony_ci		return 0;
53062306a36Sopenharmony_ci	}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	if (rk_obj->dma_attrs & DMA_ATTR_NO_KERNEL_MAPPING)
53362306a36Sopenharmony_ci		return -ENOMEM;
53462306a36Sopenharmony_ci	iosys_map_set_vaddr(map, rk_obj->kvaddr);
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci	return 0;
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_civoid rockchip_gem_prime_vunmap(struct drm_gem_object *obj,
54062306a36Sopenharmony_ci			       struct iosys_map *map)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	struct rockchip_gem_object *rk_obj = to_rockchip_obj(obj);
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	if (rk_obj->pages) {
54562306a36Sopenharmony_ci		if (map->vaddr != rk_obj->kvaddr)
54662306a36Sopenharmony_ci			vunmap(map->vaddr);
54762306a36Sopenharmony_ci		return;
54862306a36Sopenharmony_ci	}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	/* Nothing to do if allocated by DMA mapping API. */
55162306a36Sopenharmony_ci}
552