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