1 /* 2 * NVIDIA Tegra DRM GEM helper functions 3 * 4 * Copyright (C) 2012 Sascha Hauer, Pengutronix 5 * Copyright (C) 2013 NVIDIA CORPORATION, All rights reserved. 6 * 7 * Based on the GEM/CMA helpers 8 * 9 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 10 * 11 * This program is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation; either version 2 14 * of the License, or (at your option) any later version. 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 */ 20 21 #include <drm/tegra_drm.h> 22 23 #include "gem.h" 24 25 static inline struct tegra_bo *host1x_to_tegra_bo(struct host1x_bo *bo) 26 { 27 return container_of(bo, struct tegra_bo, base); 28 } 29 30 static void tegra_bo_put(struct host1x_bo *bo) 31 { 32 struct tegra_bo *obj = host1x_to_tegra_bo(bo); 33 struct drm_device *drm = obj->gem.dev; 34 35 mutex_lock(&drm->struct_mutex); 36 drm_gem_object_unreference(&obj->gem); 37 mutex_unlock(&drm->struct_mutex); 38 } 39 40 static dma_addr_t tegra_bo_pin(struct host1x_bo *bo, struct sg_table **sgt) 41 { 42 struct tegra_bo *obj = host1x_to_tegra_bo(bo); 43 44 return obj->paddr; 45 } 46 47 static void tegra_bo_unpin(struct host1x_bo *bo, struct sg_table *sgt) 48 { 49 } 50 51 static void *tegra_bo_mmap(struct host1x_bo *bo) 52 { 53 struct tegra_bo *obj = host1x_to_tegra_bo(bo); 54 55 return obj->vaddr; 56 } 57 58 static void tegra_bo_munmap(struct host1x_bo *bo, void *addr) 59 { 60 } 61 62 static void *tegra_bo_kmap(struct host1x_bo *bo, unsigned int page) 63 { 64 struct tegra_bo *obj = host1x_to_tegra_bo(bo); 65 66 return obj->vaddr + page * PAGE_SIZE; 67 } 68 69 static void tegra_bo_kunmap(struct host1x_bo *bo, unsigned int page, 70 void *addr) 71 { 72 } 73 74 static struct host1x_bo *tegra_bo_get(struct host1x_bo *bo) 75 { 76 struct tegra_bo *obj = host1x_to_tegra_bo(bo); 77 struct drm_device *drm = obj->gem.dev; 78 79 mutex_lock(&drm->struct_mutex); 80 drm_gem_object_reference(&obj->gem); 81 mutex_unlock(&drm->struct_mutex); 82 83 return bo; 84 } 85 86 const struct host1x_bo_ops tegra_bo_ops = { 87 .get = tegra_bo_get, 88 .put = tegra_bo_put, 89 .pin = tegra_bo_pin, 90 .unpin = tegra_bo_unpin, 91 .mmap = tegra_bo_mmap, 92 .munmap = tegra_bo_munmap, 93 .kmap = tegra_bo_kmap, 94 .kunmap = tegra_bo_kunmap, 95 }; 96 97 static void tegra_bo_destroy(struct drm_device *drm, struct tegra_bo *bo) 98 { 99 dma_free_writecombine(drm->dev, bo->gem.size, bo->vaddr, bo->paddr); 100 } 101 102 struct tegra_bo *tegra_bo_create(struct drm_device *drm, unsigned int size, 103 unsigned long flags) 104 { 105 struct tegra_bo *bo; 106 int err; 107 108 bo = kzalloc(sizeof(*bo), GFP_KERNEL); 109 if (!bo) 110 return ERR_PTR(-ENOMEM); 111 112 host1x_bo_init(&bo->base, &tegra_bo_ops); 113 size = round_up(size, PAGE_SIZE); 114 115 bo->vaddr = dma_alloc_writecombine(drm->dev, size, &bo->paddr, 116 GFP_KERNEL | __GFP_NOWARN); 117 if (!bo->vaddr) { 118 dev_err(drm->dev, "failed to allocate buffer with size %u\n", 119 size); 120 err = -ENOMEM; 121 goto err_dma; 122 } 123 124 err = drm_gem_object_init(drm, &bo->gem, size); 125 if (err) 126 goto err_init; 127 128 err = drm_gem_create_mmap_offset(&bo->gem); 129 if (err) 130 goto err_mmap; 131 132 if (flags & DRM_TEGRA_GEM_CREATE_TILED) 133 bo->flags |= TEGRA_BO_TILED; 134 135 if (flags & DRM_TEGRA_GEM_CREATE_BOTTOM_UP) 136 bo->flags |= TEGRA_BO_BOTTOM_UP; 137 138 return bo; 139 140 err_mmap: 141 drm_gem_object_release(&bo->gem); 142 err_init: 143 tegra_bo_destroy(drm, bo); 144 err_dma: 145 kfree(bo); 146 147 return ERR_PTR(err); 148 149 } 150 151 struct tegra_bo *tegra_bo_create_with_handle(struct drm_file *file, 152 struct drm_device *drm, 153 unsigned int size, 154 unsigned long flags, 155 unsigned int *handle) 156 { 157 struct tegra_bo *bo; 158 int ret; 159 160 bo = tegra_bo_create(drm, size, flags); 161 if (IS_ERR(bo)) 162 return bo; 163 164 ret = drm_gem_handle_create(file, &bo->gem, handle); 165 if (ret) 166 goto err; 167 168 drm_gem_object_unreference_unlocked(&bo->gem); 169 170 return bo; 171 172 err: 173 tegra_bo_free_object(&bo->gem); 174 return ERR_PTR(ret); 175 } 176 177 void tegra_bo_free_object(struct drm_gem_object *gem) 178 { 179 struct tegra_bo *bo = to_tegra_bo(gem); 180 181 drm_gem_free_mmap_offset(gem); 182 drm_gem_object_release(gem); 183 tegra_bo_destroy(gem->dev, bo); 184 185 kfree(bo); 186 } 187 188 int tegra_bo_dumb_create(struct drm_file *file, struct drm_device *drm, 189 struct drm_mode_create_dumb *args) 190 { 191 int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8); 192 struct tegra_bo *bo; 193 194 if (args->pitch < min_pitch) 195 args->pitch = min_pitch; 196 197 if (args->size < args->pitch * args->height) 198 args->size = args->pitch * args->height; 199 200 bo = tegra_bo_create_with_handle(file, drm, args->size, 0, 201 &args->handle); 202 if (IS_ERR(bo)) 203 return PTR_ERR(bo); 204 205 return 0; 206 } 207 208 int tegra_bo_dumb_map_offset(struct drm_file *file, struct drm_device *drm, 209 uint32_t handle, uint64_t *offset) 210 { 211 struct drm_gem_object *gem; 212 struct tegra_bo *bo; 213 214 mutex_lock(&drm->struct_mutex); 215 216 gem = drm_gem_object_lookup(drm, file, handle); 217 if (!gem) { 218 dev_err(drm->dev, "failed to lookup GEM object\n"); 219 mutex_unlock(&drm->struct_mutex); 220 return -EINVAL; 221 } 222 223 bo = to_tegra_bo(gem); 224 225 *offset = drm_vma_node_offset_addr(&bo->gem.vma_node); 226 227 drm_gem_object_unreference(gem); 228 229 mutex_unlock(&drm->struct_mutex); 230 231 return 0; 232 } 233 234 const struct vm_operations_struct tegra_bo_vm_ops = { 235 .open = drm_gem_vm_open, 236 .close = drm_gem_vm_close, 237 }; 238 239 int tegra_drm_mmap(struct file *file, struct vm_area_struct *vma) 240 { 241 struct drm_gem_object *gem; 242 struct tegra_bo *bo; 243 int ret; 244 245 ret = drm_gem_mmap(file, vma); 246 if (ret) 247 return ret; 248 249 gem = vma->vm_private_data; 250 bo = to_tegra_bo(gem); 251 252 ret = remap_pfn_range(vma, vma->vm_start, bo->paddr >> PAGE_SHIFT, 253 vma->vm_end - vma->vm_start, vma->vm_page_prot); 254 if (ret) 255 drm_gem_vm_close(vma); 256 257 return ret; 258 } 259