11c248b7dSInki Dae /* exynos_drm_gem.c 21c248b7dSInki Dae * 31c248b7dSInki Dae * Copyright (c) 2011 Samsung Electronics Co., Ltd. 41c248b7dSInki Dae * Author: Inki Dae <inki.dae@samsung.com> 51c248b7dSInki Dae * 6d81aecb5SInki Dae * This program is free software; you can redistribute it and/or modify it 7d81aecb5SInki Dae * under the terms of the GNU General Public License as published by the 8d81aecb5SInki Dae * Free Software Foundation; either version 2 of the License, or (at your 9d81aecb5SInki Dae * option) any later version. 101c248b7dSInki Dae */ 111c248b7dSInki Dae 12760285e7SDavid Howells #include <drm/drmP.h> 130de23977SDavid Herrmann #include <drm/drm_vma_manager.h> 141c248b7dSInki Dae 152b35892eSInki Dae #include <linux/shmem_fs.h> 1601ed50ddSJoonyoung Shim #include <linux/dma-buf.h> 1701c8f1c4SDan Williams #include <linux/pfn_t.h> 181c248b7dSInki Dae #include <drm/exynos_drm.h> 191c248b7dSInki Dae 201c248b7dSInki Dae #include "exynos_drm_drv.h" 211c248b7dSInki Dae #include "exynos_drm_gem.h" 223fec4532SVikas Sajjan #include "exynos_drm_iommu.h" 231c248b7dSInki Dae 24813fd67bSJoonyoung Shim static int exynos_drm_alloc_buf(struct exynos_drm_gem *exynos_gem) 252a8cb489SJoonyoung Shim { 26813fd67bSJoonyoung Shim struct drm_device *dev = exynos_gem->base.dev; 2700085f1eSKrzysztof Kozlowski unsigned long attr; 282a8cb489SJoonyoung Shim unsigned int nr_pages; 29df547bf7SMarek Szyprowski struct sg_table sgt; 30df547bf7SMarek Szyprowski int ret = -ENOMEM; 312a8cb489SJoonyoung Shim 32813fd67bSJoonyoung Shim if (exynos_gem->dma_addr) { 332a8cb489SJoonyoung Shim DRM_DEBUG_KMS("already allocated.\n"); 342a8cb489SJoonyoung Shim return 0; 352a8cb489SJoonyoung Shim } 362a8cb489SJoonyoung Shim 3700085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs = 0; 382a8cb489SJoonyoung Shim 392a8cb489SJoonyoung Shim /* 402a8cb489SJoonyoung Shim * if EXYNOS_BO_CONTIG, fully physically contiguous memory 412a8cb489SJoonyoung Shim * region will be allocated else physically contiguous 422a8cb489SJoonyoung Shim * as possible. 432a8cb489SJoonyoung Shim */ 44813fd67bSJoonyoung Shim if (!(exynos_gem->flags & EXYNOS_BO_NONCONTIG)) 4500085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs |= DMA_ATTR_FORCE_CONTIGUOUS; 462a8cb489SJoonyoung Shim 472a8cb489SJoonyoung Shim /* 482a8cb489SJoonyoung Shim * if EXYNOS_BO_WC or EXYNOS_BO_NONCACHABLE, writecombine mapping 492a8cb489SJoonyoung Shim * else cachable mapping. 502a8cb489SJoonyoung Shim */ 51813fd67bSJoonyoung Shim if (exynos_gem->flags & EXYNOS_BO_WC || 52813fd67bSJoonyoung Shim !(exynos_gem->flags & EXYNOS_BO_CACHABLE)) 532a8cb489SJoonyoung Shim attr = DMA_ATTR_WRITE_COMBINE; 542a8cb489SJoonyoung Shim else 552a8cb489SJoonyoung Shim attr = DMA_ATTR_NON_CONSISTENT; 562a8cb489SJoonyoung Shim 5700085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs |= attr; 5800085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs |= DMA_ATTR_NO_KERNEL_MAPPING; 592a8cb489SJoonyoung Shim 60813fd67bSJoonyoung Shim nr_pages = exynos_gem->size >> PAGE_SHIFT; 612a8cb489SJoonyoung Shim 622098105eSMichal Hocko exynos_gem->pages = kvmalloc_array(nr_pages, sizeof(struct page *), 632098105eSMichal Hocko GFP_KERNEL | __GFP_ZERO); 64813fd67bSJoonyoung Shim if (!exynos_gem->pages) { 652a8cb489SJoonyoung Shim DRM_ERROR("failed to allocate pages.\n"); 662a8cb489SJoonyoung Shim return -ENOMEM; 672a8cb489SJoonyoung Shim } 682a8cb489SJoonyoung Shim 69f43c3596SMarek Szyprowski exynos_gem->cookie = dma_alloc_attrs(to_dma_dev(dev), exynos_gem->size, 70813fd67bSJoonyoung Shim &exynos_gem->dma_addr, GFP_KERNEL, 7100085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs); 72813fd67bSJoonyoung Shim if (!exynos_gem->cookie) { 732a8cb489SJoonyoung Shim DRM_ERROR("failed to allocate buffer.\n"); 74df547bf7SMarek Szyprowski goto err_free; 752a8cb489SJoonyoung Shim } 762a8cb489SJoonyoung Shim 77f43c3596SMarek Szyprowski ret = dma_get_sgtable_attrs(to_dma_dev(dev), &sgt, exynos_gem->cookie, 78df547bf7SMarek Szyprowski exynos_gem->dma_addr, exynos_gem->size, 7900085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs); 80df547bf7SMarek Szyprowski if (ret < 0) { 81df547bf7SMarek Szyprowski DRM_ERROR("failed to get sgtable.\n"); 82df547bf7SMarek Szyprowski goto err_dma_free; 83df547bf7SMarek Szyprowski } 84333e8e58SJoonyoung Shim 85df547bf7SMarek Szyprowski if (drm_prime_sg_to_page_addr_arrays(&sgt, exynos_gem->pages, NULL, 86df547bf7SMarek Szyprowski nr_pages)) { 87df547bf7SMarek Szyprowski DRM_ERROR("invalid sgtable.\n"); 88df547bf7SMarek Szyprowski ret = -EINVAL; 89df547bf7SMarek Szyprowski goto err_sgt_free; 902a8cb489SJoonyoung Shim } 91df547bf7SMarek Szyprowski 92df547bf7SMarek Szyprowski sg_free_table(&sgt); 932a8cb489SJoonyoung Shim 942a8cb489SJoonyoung Shim DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", 95813fd67bSJoonyoung Shim (unsigned long)exynos_gem->dma_addr, exynos_gem->size); 962a8cb489SJoonyoung Shim 972a8cb489SJoonyoung Shim return 0; 98df547bf7SMarek Szyprowski 99df547bf7SMarek Szyprowski err_sgt_free: 100df547bf7SMarek Szyprowski sg_free_table(&sgt); 101df547bf7SMarek Szyprowski err_dma_free: 102f43c3596SMarek Szyprowski dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, 10300085f1eSKrzysztof Kozlowski exynos_gem->dma_addr, exynos_gem->dma_attrs); 104df547bf7SMarek Szyprowski err_free: 1052098105eSMichal Hocko kvfree(exynos_gem->pages); 106df547bf7SMarek Szyprowski 107df547bf7SMarek Szyprowski return ret; 1082a8cb489SJoonyoung Shim } 1092a8cb489SJoonyoung Shim 110813fd67bSJoonyoung Shim static void exynos_drm_free_buf(struct exynos_drm_gem *exynos_gem) 1112a8cb489SJoonyoung Shim { 112813fd67bSJoonyoung Shim struct drm_device *dev = exynos_gem->base.dev; 1132a8cb489SJoonyoung Shim 114813fd67bSJoonyoung Shim if (!exynos_gem->dma_addr) { 1152a8cb489SJoonyoung Shim DRM_DEBUG_KMS("dma_addr is invalid.\n"); 1162a8cb489SJoonyoung Shim return; 1172a8cb489SJoonyoung Shim } 1182a8cb489SJoonyoung Shim 1192a8cb489SJoonyoung Shim DRM_DEBUG_KMS("dma_addr(0x%lx), size(0x%lx)\n", 120813fd67bSJoonyoung Shim (unsigned long)exynos_gem->dma_addr, exynos_gem->size); 1212a8cb489SJoonyoung Shim 122f43c3596SMarek Szyprowski dma_free_attrs(to_dma_dev(dev), exynos_gem->size, exynos_gem->cookie, 123813fd67bSJoonyoung Shim (dma_addr_t)exynos_gem->dma_addr, 12400085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs); 125333e8e58SJoonyoung Shim 1262098105eSMichal Hocko kvfree(exynos_gem->pages); 1272a8cb489SJoonyoung Shim } 1282a8cb489SJoonyoung Shim 1292364839aSJoonyoung Shim static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, 1302364839aSJoonyoung Shim struct drm_file *file_priv, 1312364839aSJoonyoung Shim unsigned int *handle) 1321c248b7dSInki Dae { 1331c248b7dSInki Dae int ret; 1341c248b7dSInki Dae 1351c248b7dSInki Dae /* 1361c248b7dSInki Dae * allocate a id of idr table where the obj is registered 1371c248b7dSInki Dae * and handle has the id what user can see. 1381c248b7dSInki Dae */ 1391c248b7dSInki Dae ret = drm_gem_handle_create(file_priv, obj, handle); 1401c248b7dSInki Dae if (ret) 1412364839aSJoonyoung Shim return ret; 1421c248b7dSInki Dae 1431c248b7dSInki Dae DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); 1441c248b7dSInki Dae 1451c248b7dSInki Dae /* drop reference from allocate - handle holds it now. */ 1461c248b7dSInki Dae drm_gem_object_unreference_unlocked(obj); 1471c248b7dSInki Dae 1482364839aSJoonyoung Shim return 0; 1492364839aSJoonyoung Shim } 1501c248b7dSInki Dae 151813fd67bSJoonyoung Shim void exynos_drm_gem_destroy(struct exynos_drm_gem *exynos_gem) 1522364839aSJoonyoung Shim { 153813fd67bSJoonyoung Shim struct drm_gem_object *obj = &exynos_gem->base; 1542364839aSJoonyoung Shim 155a8e11d1cSDaniel Vetter DRM_DEBUG_KMS("handle count = %d\n", obj->handle_count); 1562364839aSJoonyoung Shim 157c374e731SInki Dae /* 158c374e731SInki Dae * do not release memory region from exporter. 159c374e731SInki Dae * 160c374e731SInki Dae * the region will be released by exporter 161c374e731SInki Dae * once dmabuf's refcount becomes 0. 162c374e731SInki Dae */ 163c374e731SInki Dae if (obj->import_attach) 164813fd67bSJoonyoung Shim drm_prime_gem_destroy(obj, exynos_gem->sgt); 1657c93537aSJoonyoung Shim else 166813fd67bSJoonyoung Shim exynos_drm_free_buf(exynos_gem); 1672b35892eSInki Dae 1682364839aSJoonyoung Shim /* release file pointer to gem object. */ 1691c248b7dSInki Dae drm_gem_object_release(obj); 1701c248b7dSInki Dae 171813fd67bSJoonyoung Shim kfree(exynos_gem); 1722364839aSJoonyoung Shim } 1732364839aSJoonyoung Shim 174a4f19aaaSInki Dae unsigned long exynos_drm_gem_get_size(struct drm_device *dev, 175a4f19aaaSInki Dae unsigned int gem_handle, 176a4f19aaaSInki Dae struct drm_file *file_priv) 177a4f19aaaSInki Dae { 178813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 179a4f19aaaSInki Dae struct drm_gem_object *obj; 180a4f19aaaSInki Dae 181a8ad0bd8SChris Wilson obj = drm_gem_object_lookup(file_priv, gem_handle); 182a4f19aaaSInki Dae if (!obj) { 183a4f19aaaSInki Dae DRM_ERROR("failed to lookup gem object.\n"); 184a4f19aaaSInki Dae return 0; 185a4f19aaaSInki Dae } 186a4f19aaaSInki Dae 187813fd67bSJoonyoung Shim exynos_gem = to_exynos_gem(obj); 188a4f19aaaSInki Dae 189a4f19aaaSInki Dae drm_gem_object_unreference_unlocked(obj); 190a4f19aaaSInki Dae 191813fd67bSJoonyoung Shim return exynos_gem->size; 192a4f19aaaSInki Dae } 193a4f19aaaSInki Dae 194813fd67bSJoonyoung Shim static struct exynos_drm_gem *exynos_drm_gem_init(struct drm_device *dev, 1952364839aSJoonyoung Shim unsigned long size) 1962364839aSJoonyoung Shim { 197813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 1982364839aSJoonyoung Shim struct drm_gem_object *obj; 1992364839aSJoonyoung Shim int ret; 2002364839aSJoonyoung Shim 201813fd67bSJoonyoung Shim exynos_gem = kzalloc(sizeof(*exynos_gem), GFP_KERNEL); 202813fd67bSJoonyoung Shim if (!exynos_gem) 2035f3f4266SJoonyoung Shim return ERR_PTR(-ENOMEM); 2042364839aSJoonyoung Shim 205813fd67bSJoonyoung Shim exynos_gem->size = size; 206813fd67bSJoonyoung Shim obj = &exynos_gem->base; 2072364839aSJoonyoung Shim 2082364839aSJoonyoung Shim ret = drm_gem_object_init(dev, obj, size); 2092364839aSJoonyoung Shim if (ret < 0) { 2102364839aSJoonyoung Shim DRM_ERROR("failed to initialize gem object\n"); 211813fd67bSJoonyoung Shim kfree(exynos_gem); 2125f3f4266SJoonyoung Shim return ERR_PTR(ret); 2132364839aSJoonyoung Shim } 2142364839aSJoonyoung Shim 21548cf53f4SJoonyoung Shim ret = drm_gem_create_mmap_offset(obj); 21648cf53f4SJoonyoung Shim if (ret < 0) { 21748cf53f4SJoonyoung Shim drm_gem_object_release(obj); 218813fd67bSJoonyoung Shim kfree(exynos_gem); 21948cf53f4SJoonyoung Shim return ERR_PTR(ret); 22048cf53f4SJoonyoung Shim } 22148cf53f4SJoonyoung Shim 2229cdf0ed2SKrzysztof Kozlowski DRM_DEBUG_KMS("created file object = %pK\n", obj->filp); 2232364839aSJoonyoung Shim 224813fd67bSJoonyoung Shim return exynos_gem; 2251c248b7dSInki Dae } 2261c248b7dSInki Dae 227813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_drm_gem_create(struct drm_device *dev, 2282b35892eSInki Dae unsigned int flags, 229ee5e770eSJoonyoung Shim unsigned long size) 230f088d5a9SInki Dae { 231813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 2322b35892eSInki Dae int ret; 233f088d5a9SInki Dae 234c4130bcdSJoonyoung Shim if (flags & ~(EXYNOS_BO_MASK)) { 23563eb0a12SShuah Khan DRM_ERROR("invalid GEM buffer flags: %u\n", flags); 236c4130bcdSJoonyoung Shim return ERR_PTR(-EINVAL); 237c4130bcdSJoonyoung Shim } 238c4130bcdSJoonyoung Shim 239dcf9af82SInki Dae if (!size) { 24063eb0a12SShuah Khan DRM_ERROR("invalid GEM buffer size: %lu\n", size); 241dcf9af82SInki Dae return ERR_PTR(-EINVAL); 242dcf9af82SInki Dae } 243f088d5a9SInki Dae 244eb57da88SJoonyoung Shim size = roundup(size, PAGE_SIZE); 245dcf9af82SInki Dae 246813fd67bSJoonyoung Shim exynos_gem = exynos_drm_gem_init(dev, size); 247813fd67bSJoonyoung Shim if (IS_ERR(exynos_gem)) 248813fd67bSJoonyoung Shim return exynos_gem; 2492b35892eSInki Dae 2502b35892eSInki Dae /* set memory type and cache attribute from user side. */ 251813fd67bSJoonyoung Shim exynos_gem->flags = flags; 2522b35892eSInki Dae 253813fd67bSJoonyoung Shim ret = exynos_drm_alloc_buf(exynos_gem); 2542a8cb489SJoonyoung Shim if (ret < 0) { 255813fd67bSJoonyoung Shim drm_gem_object_release(&exynos_gem->base); 256813fd67bSJoonyoung Shim kfree(exynos_gem); 2572b35892eSInki Dae return ERR_PTR(ret); 258f088d5a9SInki Dae } 259f088d5a9SInki Dae 260813fd67bSJoonyoung Shim return exynos_gem; 2612a8cb489SJoonyoung Shim } 2622a8cb489SJoonyoung Shim 2631c248b7dSInki Dae int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, 2641c248b7dSInki Dae struct drm_file *file_priv) 2651c248b7dSInki Dae { 2661c248b7dSInki Dae struct drm_exynos_gem_create *args = data; 267813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 2682364839aSJoonyoung Shim int ret; 2691c248b7dSInki Dae 270813fd67bSJoonyoung Shim exynos_gem = exynos_drm_gem_create(dev, args->flags, args->size); 271813fd67bSJoonyoung Shim if (IS_ERR(exynos_gem)) 272813fd67bSJoonyoung Shim return PTR_ERR(exynos_gem); 2731c248b7dSInki Dae 274813fd67bSJoonyoung Shim ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, 2752364839aSJoonyoung Shim &args->handle); 2762364839aSJoonyoung Shim if (ret) { 277813fd67bSJoonyoung Shim exynos_drm_gem_destroy(exynos_gem); 2782364839aSJoonyoung Shim return ret; 2792364839aSJoonyoung Shim } 2802364839aSJoonyoung Shim 2811c248b7dSInki Dae return 0; 2821c248b7dSInki Dae } 2831c248b7dSInki Dae 2846564c65fSJoonyoung Shim int exynos_drm_gem_map_ioctl(struct drm_device *dev, void *data, 2856564c65fSJoonyoung Shim struct drm_file *file_priv) 2866564c65fSJoonyoung Shim { 2876564c65fSJoonyoung Shim struct drm_exynos_gem_map *args = data; 2886564c65fSJoonyoung Shim 2894d12c233SNoralf Trønnes return drm_gem_dumb_map_offset(file_priv, dev, args->handle, 2906564c65fSJoonyoung Shim &args->offset); 2916564c65fSJoonyoung Shim } 2926564c65fSJoonyoung Shim 293d87342c1SInki Dae dma_addr_t *exynos_drm_gem_get_dma_addr(struct drm_device *dev, 294f0b1bda7SInki Dae unsigned int gem_handle, 295d87342c1SInki Dae struct drm_file *filp) 296f0b1bda7SInki Dae { 297813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 298f0b1bda7SInki Dae struct drm_gem_object *obj; 299f0b1bda7SInki Dae 300a8ad0bd8SChris Wilson obj = drm_gem_object_lookup(filp, gem_handle); 301f0b1bda7SInki Dae if (!obj) { 302f0b1bda7SInki Dae DRM_ERROR("failed to lookup gem object.\n"); 303f0b1bda7SInki Dae return ERR_PTR(-EINVAL); 304f0b1bda7SInki Dae } 305f0b1bda7SInki Dae 306813fd67bSJoonyoung Shim exynos_gem = to_exynos_gem(obj); 307f0b1bda7SInki Dae 308813fd67bSJoonyoung Shim return &exynos_gem->dma_addr; 309f0b1bda7SInki Dae } 310f0b1bda7SInki Dae 311f0b1bda7SInki Dae void exynos_drm_gem_put_dma_addr(struct drm_device *dev, 312f0b1bda7SInki Dae unsigned int gem_handle, 313d87342c1SInki Dae struct drm_file *filp) 314f0b1bda7SInki Dae { 315f0b1bda7SInki Dae struct drm_gem_object *obj; 316f0b1bda7SInki Dae 317a8ad0bd8SChris Wilson obj = drm_gem_object_lookup(filp, gem_handle); 318f0b1bda7SInki Dae if (!obj) { 319f0b1bda7SInki Dae DRM_ERROR("failed to lookup gem object.\n"); 320f0b1bda7SInki Dae return; 321f0b1bda7SInki Dae } 322f0b1bda7SInki Dae 323f0b1bda7SInki Dae drm_gem_object_unreference_unlocked(obj); 324f0b1bda7SInki Dae 325f0b1bda7SInki Dae /* 326f0b1bda7SInki Dae * decrease obj->refcount one more time because we has already 327f0b1bda7SInki Dae * increased it at exynos_drm_gem_get_dma_addr(). 328f0b1bda7SInki Dae */ 329f0b1bda7SInki Dae drm_gem_object_unreference_unlocked(obj); 330f0b1bda7SInki Dae } 331f0b1bda7SInki Dae 332813fd67bSJoonyoung Shim static int exynos_drm_gem_mmap_buffer(struct exynos_drm_gem *exynos_gem, 3331c248b7dSInki Dae struct vm_area_struct *vma) 3341c248b7dSInki Dae { 335813fd67bSJoonyoung Shim struct drm_device *drm_dev = exynos_gem->base.dev; 3360519f9a1SInki Dae unsigned long vm_size; 3375b07c660SInki Dae int ret; 3381c248b7dSInki Dae 339832316c7SInki Dae vma->vm_flags &= ~VM_PFNMAP; 340832316c7SInki Dae vma->vm_pgoff = 0; 3411c248b7dSInki Dae 3420519f9a1SInki Dae vm_size = vma->vm_end - vma->vm_start; 3432b35892eSInki Dae 3441c248b7dSInki Dae /* check if user-requested size is valid. */ 345813fd67bSJoonyoung Shim if (vm_size > exynos_gem->size) 3461c248b7dSInki Dae return -EINVAL; 3471c248b7dSInki Dae 348f43c3596SMarek Szyprowski ret = dma_mmap_attrs(to_dma_dev(drm_dev), vma, exynos_gem->cookie, 349813fd67bSJoonyoung Shim exynos_gem->dma_addr, exynos_gem->size, 35000085f1eSKrzysztof Kozlowski exynos_gem->dma_attrs); 3515b07c660SInki Dae if (ret < 0) { 3525b07c660SInki Dae DRM_ERROR("failed to mmap.\n"); 3535b07c660SInki Dae return ret; 3545b07c660SInki Dae } 3555b07c660SInki Dae 3561c248b7dSInki Dae return 0; 3571c248b7dSInki Dae } 3581c248b7dSInki Dae 35940cd7e0cSInki Dae int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data, 36040cd7e0cSInki Dae struct drm_file *file_priv) 361b4cfd4ddSJoonyoung Shim { 362813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 36340cd7e0cSInki Dae struct drm_exynos_gem_info *args = data; 36440cd7e0cSInki Dae struct drm_gem_object *obj; 36540cd7e0cSInki Dae 366a8ad0bd8SChris Wilson obj = drm_gem_object_lookup(file_priv, args->handle); 36740cd7e0cSInki Dae if (!obj) { 36840cd7e0cSInki Dae DRM_ERROR("failed to lookup gem object.\n"); 36940cd7e0cSInki Dae return -EINVAL; 37040cd7e0cSInki Dae } 37140cd7e0cSInki Dae 372813fd67bSJoonyoung Shim exynos_gem = to_exynos_gem(obj); 37340cd7e0cSInki Dae 374813fd67bSJoonyoung Shim args->flags = exynos_gem->flags; 375813fd67bSJoonyoung Shim args->size = exynos_gem->size; 37640cd7e0cSInki Dae 3776c991a22SDaniel Vetter drm_gem_object_unreference_unlocked(obj); 37840cd7e0cSInki Dae 37940cd7e0cSInki Dae return 0; 38040cd7e0cSInki Dae } 38140cd7e0cSInki Dae 382ee5e770eSJoonyoung Shim void exynos_drm_gem_free_object(struct drm_gem_object *obj) 3831c248b7dSInki Dae { 384813fd67bSJoonyoung Shim exynos_drm_gem_destroy(to_exynos_gem(obj)); 3851c248b7dSInki Dae } 3861c248b7dSInki Dae 3871c248b7dSInki Dae int exynos_drm_gem_dumb_create(struct drm_file *file_priv, 388ee5e770eSJoonyoung Shim struct drm_device *dev, 389ee5e770eSJoonyoung Shim struct drm_mode_create_dumb *args) 3901c248b7dSInki Dae { 391813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 392333e8e58SJoonyoung Shim unsigned int flags; 3932364839aSJoonyoung Shim int ret; 3941c248b7dSInki Dae 3951c248b7dSInki Dae /* 396c6b78bc8SMasanari Iida * allocate memory to be used for framebuffer. 3971c248b7dSInki Dae * - this callback would be called by user application 3981c248b7dSInki Dae * with DRM_IOCTL_MODE_CREATE_DUMB command. 3991c248b7dSInki Dae */ 4001c248b7dSInki Dae 4013fd6b694SCooper Yuan args->pitch = args->width * ((args->bpp + 7) / 8); 4027da5907cSInki Dae args->size = args->pitch * args->height; 4031c248b7dSInki Dae 404333e8e58SJoonyoung Shim if (is_drm_iommu_supported(dev)) 405333e8e58SJoonyoung Shim flags = EXYNOS_BO_NONCONTIG | EXYNOS_BO_WC; 406333e8e58SJoonyoung Shim else 407333e8e58SJoonyoung Shim flags = EXYNOS_BO_CONTIG | EXYNOS_BO_WC; 4083fec4532SVikas Sajjan 409813fd67bSJoonyoung Shim exynos_gem = exynos_drm_gem_create(dev, flags, args->size); 410813fd67bSJoonyoung Shim if (IS_ERR(exynos_gem)) { 411122beea8SRahul Sharma dev_warn(dev->dev, "FB allocation failed.\n"); 412813fd67bSJoonyoung Shim return PTR_ERR(exynos_gem); 413122beea8SRahul Sharma } 4141c248b7dSInki Dae 415813fd67bSJoonyoung Shim ret = exynos_drm_gem_handle_create(&exynos_gem->base, file_priv, 4162364839aSJoonyoung Shim &args->handle); 4172364839aSJoonyoung Shim if (ret) { 418813fd67bSJoonyoung Shim exynos_drm_gem_destroy(exynos_gem); 4192364839aSJoonyoung Shim return ret; 4202364839aSJoonyoung Shim } 4212364839aSJoonyoung Shim 4221c248b7dSInki Dae return 0; 4231c248b7dSInki Dae } 4241c248b7dSInki Dae 42511bac800SDave Jiang int exynos_drm_gem_fault(struct vm_fault *vmf) 4261c248b7dSInki Dae { 42711bac800SDave Jiang struct vm_area_struct *vma = vmf->vma; 4281c248b7dSInki Dae struct drm_gem_object *obj = vma->vm_private_data; 429813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); 4300e9a2ee3SJoonyoung Shim unsigned long pfn; 4311c248b7dSInki Dae pgoff_t page_offset; 4321c248b7dSInki Dae int ret; 4331c248b7dSInki Dae 4341a29d85eSJan Kara page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT; 4351c248b7dSInki Dae 436813fd67bSJoonyoung Shim if (page_offset >= (exynos_gem->size >> PAGE_SHIFT)) { 4370e9a2ee3SJoonyoung Shim DRM_ERROR("invalid page offset\n"); 4380e9a2ee3SJoonyoung Shim ret = -EINVAL; 4390e9a2ee3SJoonyoung Shim goto out; 4400e9a2ee3SJoonyoung Shim } 4411c248b7dSInki Dae 442813fd67bSJoonyoung Shim pfn = page_to_pfn(exynos_gem->pages[page_offset]); 4431a29d85eSJan Kara ret = vm_insert_mixed(vma, vmf->address, __pfn_to_pfn_t(pfn, PFN_DEV)); 4440e9a2ee3SJoonyoung Shim 4450e9a2ee3SJoonyoung Shim out: 44623597e26SJoonyoung Shim switch (ret) { 44723597e26SJoonyoung Shim case 0: 44823597e26SJoonyoung Shim case -ERESTARTSYS: 44923597e26SJoonyoung Shim case -EINTR: 45023597e26SJoonyoung Shim return VM_FAULT_NOPAGE; 45123597e26SJoonyoung Shim case -ENOMEM: 45223597e26SJoonyoung Shim return VM_FAULT_OOM; 45323597e26SJoonyoung Shim default: 45423597e26SJoonyoung Shim return VM_FAULT_SIGBUS; 45523597e26SJoonyoung Shim } 4561c248b7dSInki Dae } 4571c248b7dSInki Dae 4585a0202f7SJoonyoung Shim static int exynos_drm_gem_mmap_obj(struct drm_gem_object *obj, 4595a0202f7SJoonyoung Shim struct vm_area_struct *vma) 4601c248b7dSInki Dae { 4615a0202f7SJoonyoung Shim struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); 4621c248b7dSInki Dae int ret; 4631c248b7dSInki Dae 464813fd67bSJoonyoung Shim DRM_DEBUG_KMS("flags = 0x%x\n", exynos_gem->flags); 465211b8878SJoonyoung Shim 466211b8878SJoonyoung Shim /* non-cachable as default. */ 467813fd67bSJoonyoung Shim if (exynos_gem->flags & EXYNOS_BO_CACHABLE) 468211b8878SJoonyoung Shim vma->vm_page_prot = vm_get_page_prot(vma->vm_flags); 469813fd67bSJoonyoung Shim else if (exynos_gem->flags & EXYNOS_BO_WC) 470211b8878SJoonyoung Shim vma->vm_page_prot = 471211b8878SJoonyoung Shim pgprot_writecombine(vm_get_page_prot(vma->vm_flags)); 472211b8878SJoonyoung Shim else 473211b8878SJoonyoung Shim vma->vm_page_prot = 474211b8878SJoonyoung Shim pgprot_noncached(vm_get_page_prot(vma->vm_flags)); 475c01d73faSInki Dae 476813fd67bSJoonyoung Shim ret = exynos_drm_gem_mmap_buffer(exynos_gem, vma); 477832316c7SInki Dae if (ret) 478832316c7SInki Dae goto err_close_vm; 479832316c7SInki Dae 480832316c7SInki Dae return ret; 481832316c7SInki Dae 482832316c7SInki Dae err_close_vm: 483832316c7SInki Dae drm_gem_vm_close(vma); 484832316c7SInki Dae 4851c248b7dSInki Dae return ret; 4861c248b7dSInki Dae } 48701ed50ddSJoonyoung Shim 4885a0202f7SJoonyoung Shim int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) 4895a0202f7SJoonyoung Shim { 4905a0202f7SJoonyoung Shim struct drm_gem_object *obj; 4915a0202f7SJoonyoung Shim int ret; 4925a0202f7SJoonyoung Shim 4935a0202f7SJoonyoung Shim /* set vm_area_struct. */ 4945a0202f7SJoonyoung Shim ret = drm_gem_mmap(filp, vma); 4955a0202f7SJoonyoung Shim if (ret < 0) { 4965a0202f7SJoonyoung Shim DRM_ERROR("failed to mmap.\n"); 4975a0202f7SJoonyoung Shim return ret; 4985a0202f7SJoonyoung Shim } 4995a0202f7SJoonyoung Shim 5005a0202f7SJoonyoung Shim obj = vma->vm_private_data; 5015a0202f7SJoonyoung Shim 50255b19fc7SJoonyoung Shim if (obj->import_attach) 50355b19fc7SJoonyoung Shim return dma_buf_mmap(obj->dma_buf, vma, 0); 50455b19fc7SJoonyoung Shim 5055a0202f7SJoonyoung Shim return exynos_drm_gem_mmap_obj(obj, vma); 5065a0202f7SJoonyoung Shim } 5075a0202f7SJoonyoung Shim 50801ed50ddSJoonyoung Shim /* low-level interface prime helpers */ 50901ed50ddSJoonyoung Shim struct sg_table *exynos_drm_gem_prime_get_sg_table(struct drm_gem_object *obj) 51001ed50ddSJoonyoung Shim { 511813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem = to_exynos_gem(obj); 51201ed50ddSJoonyoung Shim int npages; 51301ed50ddSJoonyoung Shim 514813fd67bSJoonyoung Shim npages = exynos_gem->size >> PAGE_SHIFT; 51501ed50ddSJoonyoung Shim 516813fd67bSJoonyoung Shim return drm_prime_pages_to_sg(exynos_gem->pages, npages); 51701ed50ddSJoonyoung Shim } 51801ed50ddSJoonyoung Shim 51901ed50ddSJoonyoung Shim struct drm_gem_object * 52001ed50ddSJoonyoung Shim exynos_drm_gem_prime_import_sg_table(struct drm_device *dev, 52101ed50ddSJoonyoung Shim struct dma_buf_attachment *attach, 52201ed50ddSJoonyoung Shim struct sg_table *sgt) 52301ed50ddSJoonyoung Shim { 524813fd67bSJoonyoung Shim struct exynos_drm_gem *exynos_gem; 52501ed50ddSJoonyoung Shim int npages; 52601ed50ddSJoonyoung Shim int ret; 52701ed50ddSJoonyoung Shim 528813fd67bSJoonyoung Shim exynos_gem = exynos_drm_gem_init(dev, attach->dmabuf->size); 529813fd67bSJoonyoung Shim if (IS_ERR(exynos_gem)) { 530813fd67bSJoonyoung Shim ret = PTR_ERR(exynos_gem); 53150002d4cSInki Dae return ERR_PTR(ret); 53201ed50ddSJoonyoung Shim } 53301ed50ddSJoonyoung Shim 534813fd67bSJoonyoung Shim exynos_gem->dma_addr = sg_dma_address(sgt->sgl); 5352a8cb489SJoonyoung Shim 536813fd67bSJoonyoung Shim npages = exynos_gem->size >> PAGE_SHIFT; 5372098105eSMichal Hocko exynos_gem->pages = kvmalloc_array(npages, sizeof(struct page *), GFP_KERNEL); 538813fd67bSJoonyoung Shim if (!exynos_gem->pages) { 5392a8cb489SJoonyoung Shim ret = -ENOMEM; 5402a8cb489SJoonyoung Shim goto err; 5412a8cb489SJoonyoung Shim } 5422a8cb489SJoonyoung Shim 543813fd67bSJoonyoung Shim ret = drm_prime_sg_to_page_addr_arrays(sgt, exynos_gem->pages, NULL, 5442a8cb489SJoonyoung Shim npages); 5452a8cb489SJoonyoung Shim if (ret < 0) 5462a8cb489SJoonyoung Shim goto err_free_large; 5472a8cb489SJoonyoung Shim 548813fd67bSJoonyoung Shim exynos_gem->sgt = sgt; 5497c93537aSJoonyoung Shim 55001ed50ddSJoonyoung Shim if (sgt->nents == 1) { 55101ed50ddSJoonyoung Shim /* always physically continuous memory if sgt->nents is 1. */ 552813fd67bSJoonyoung Shim exynos_gem->flags |= EXYNOS_BO_CONTIG; 55301ed50ddSJoonyoung Shim } else { 55401ed50ddSJoonyoung Shim /* 55501ed50ddSJoonyoung Shim * this case could be CONTIG or NONCONTIG type but for now 55601ed50ddSJoonyoung Shim * sets NONCONTIG. 55701ed50ddSJoonyoung Shim * TODO. we have to find a way that exporter can notify 55801ed50ddSJoonyoung Shim * the type of its own buffer to importer. 55901ed50ddSJoonyoung Shim */ 560813fd67bSJoonyoung Shim exynos_gem->flags |= EXYNOS_BO_NONCONTIG; 56101ed50ddSJoonyoung Shim } 56201ed50ddSJoonyoung Shim 563813fd67bSJoonyoung Shim return &exynos_gem->base; 56401ed50ddSJoonyoung Shim 56501ed50ddSJoonyoung Shim err_free_large: 5662098105eSMichal Hocko kvfree(exynos_gem->pages); 56701ed50ddSJoonyoung Shim err: 568813fd67bSJoonyoung Shim drm_gem_object_release(&exynos_gem->base); 569813fd67bSJoonyoung Shim kfree(exynos_gem); 57001ed50ddSJoonyoung Shim return ERR_PTR(ret); 57101ed50ddSJoonyoung Shim } 57201ed50ddSJoonyoung Shim 57301ed50ddSJoonyoung Shim void *exynos_drm_gem_prime_vmap(struct drm_gem_object *obj) 57401ed50ddSJoonyoung Shim { 57501ed50ddSJoonyoung Shim return NULL; 57601ed50ddSJoonyoung Shim } 57701ed50ddSJoonyoung Shim 57801ed50ddSJoonyoung Shim void exynos_drm_gem_prime_vunmap(struct drm_gem_object *obj, void *vaddr) 57901ed50ddSJoonyoung Shim { 58001ed50ddSJoonyoung Shim /* Nothing to do */ 58101ed50ddSJoonyoung Shim } 5825a0202f7SJoonyoung Shim 5835a0202f7SJoonyoung Shim int exynos_drm_gem_prime_mmap(struct drm_gem_object *obj, 5845a0202f7SJoonyoung Shim struct vm_area_struct *vma) 5855a0202f7SJoonyoung Shim { 5865a0202f7SJoonyoung Shim int ret; 5875a0202f7SJoonyoung Shim 5885a0202f7SJoonyoung Shim ret = drm_gem_mmap_obj(obj, obj->size, vma); 5895a0202f7SJoonyoung Shim if (ret < 0) 5905a0202f7SJoonyoung Shim return ret; 5915a0202f7SJoonyoung Shim 5925a0202f7SJoonyoung Shim return exynos_drm_gem_mmap_obj(obj, vma); 5935a0202f7SJoonyoung Shim } 594