1/* exynos_drm_gem.c 2 * 3 * Copyright (c) 2011 Samsung Electronics Co., Ltd. 4 * Author: Inki Dae <inki.dae@samsung.com> 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a 7 * copy of this software and associated documentation files (the "Software"), 8 * to deal in the Software without restriction, including without limitation 9 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 10 * and/or sell copies of the Software, and to permit persons to whom the 11 * Software is furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the next 14 * paragraph) shall be included in all copies or substantial portions of the 15 * Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 20 * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 23 * OTHER DEALINGS IN THE SOFTWARE. 24 */ 25 26#include "drmP.h" 27#include "drm.h" 28 29#include <drm/exynos_drm.h> 30 31#include "exynos_drm_drv.h" 32#include "exynos_drm_gem.h" 33#include "exynos_drm_buf.h" 34 35static unsigned int convert_to_vm_err_msg(int msg) 36{ 37 unsigned int out_msg; 38 39 switch (msg) { 40 case 0: 41 case -ERESTARTSYS: 42 case -EINTR: 43 out_msg = VM_FAULT_NOPAGE; 44 break; 45 46 case -ENOMEM: 47 out_msg = VM_FAULT_OOM; 48 break; 49 50 default: 51 out_msg = VM_FAULT_SIGBUS; 52 break; 53 } 54 55 return out_msg; 56} 57 58static int exynos_drm_gem_handle_create(struct drm_gem_object *obj, 59 struct drm_file *file_priv, 60 unsigned int *handle) 61{ 62 int ret; 63 64 /* 65 * allocate a id of idr table where the obj is registered 66 * and handle has the id what user can see. 67 */ 68 ret = drm_gem_handle_create(file_priv, obj, handle); 69 if (ret) 70 return ret; 71 72 DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle); 73 74 /* drop reference from allocate - handle holds it now. */ 75 drm_gem_object_unreference_unlocked(obj); 76 77 return 0; 78} 79 80void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj) 81{ 82 struct drm_gem_object *obj; 83 84 DRM_DEBUG_KMS("%s\n", __FILE__); 85 86 if (!exynos_gem_obj) 87 return; 88 89 obj = &exynos_gem_obj->base; 90 91 DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count)); 92 93 exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer); 94 95 if (obj->map_list.map) 96 drm_gem_free_mmap_offset(obj); 97 98 /* release file pointer to gem object. */ 99 drm_gem_object_release(obj); 100 101 kfree(exynos_gem_obj); 102} 103 104static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev, 105 unsigned long size) 106{ 107 struct exynos_drm_gem_obj *exynos_gem_obj; 108 struct drm_gem_object *obj; 109 int ret; 110 111 exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL); 112 if (!exynos_gem_obj) { 113 DRM_ERROR("failed to allocate exynos gem object\n"); 114 return NULL; 115 } 116 117 obj = &exynos_gem_obj->base; 118 119 ret = drm_gem_object_init(dev, obj, size); 120 if (ret < 0) { 121 DRM_ERROR("failed to initialize gem object\n"); 122 kfree(exynos_gem_obj); 123 return NULL; 124 } 125 126 DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp); 127 128 return exynos_gem_obj; 129} 130 131struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev, 132 unsigned long size) 133{ 134 struct exynos_drm_gem_buf *buffer; 135 struct exynos_drm_gem_obj *exynos_gem_obj; 136 137 size = roundup(size, PAGE_SIZE); 138 DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size); 139 140 buffer = exynos_drm_buf_create(dev, size); 141 if (!buffer) 142 return ERR_PTR(-ENOMEM); 143 144 exynos_gem_obj = exynos_drm_gem_init(dev, size); 145 if (!exynos_gem_obj) { 146 exynos_drm_buf_destroy(dev, buffer); 147 return ERR_PTR(-ENOMEM); 148 } 149 150 exynos_gem_obj->buffer = buffer; 151 152 return exynos_gem_obj; 153} 154 155int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data, 156 struct drm_file *file_priv) 157{ 158 struct drm_exynos_gem_create *args = data; 159 struct exynos_drm_gem_obj *exynos_gem_obj; 160 int ret; 161 162 DRM_DEBUG_KMS("%s\n", __FILE__); 163 164 exynos_gem_obj = exynos_drm_gem_create(dev, args->size); 165 if (IS_ERR(exynos_gem_obj)) 166 return PTR_ERR(exynos_gem_obj); 167 168 ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, 169 &args->handle); 170 if (ret) { 171 exynos_drm_gem_destroy(exynos_gem_obj); 172 return ret; 173 } 174 175 return 0; 176} 177 178int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data, 179 struct drm_file *file_priv) 180{ 181 struct drm_exynos_gem_map_off *args = data; 182 183 DRM_DEBUG_KMS("%s\n", __FILE__); 184 185 DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n", 186 args->handle, (unsigned long)args->offset); 187 188 if (!(dev->driver->driver_features & DRIVER_GEM)) { 189 DRM_ERROR("does not support GEM.\n"); 190 return -ENODEV; 191 } 192 193 return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle, 194 &args->offset); 195} 196 197static int exynos_drm_gem_mmap_buffer(struct file *filp, 198 struct vm_area_struct *vma) 199{ 200 struct drm_gem_object *obj = filp->private_data; 201 struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); 202 struct exynos_drm_gem_buf *buffer; 203 unsigned long pfn, vm_size; 204 205 DRM_DEBUG_KMS("%s\n", __FILE__); 206 207 vma->vm_flags |= (VM_IO | VM_RESERVED); 208 209 /* in case of direct mapping, always having non-cachable attribute */ 210 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); 211 vma->vm_file = filp; 212 213 vm_size = vma->vm_end - vma->vm_start; 214 /* 215 * a buffer contains information to physically continuous memory 216 * allocated by user request or at framebuffer creation. 217 */ 218 buffer = exynos_gem_obj->buffer; 219 220 /* check if user-requested size is valid. */ 221 if (vm_size > buffer->size) 222 return -EINVAL; 223 224 /* 225 * get page frame number to physical memory to be mapped 226 * to user space. 227 */ 228 pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT; 229 230 DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn); 231 232 if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size, 233 vma->vm_page_prot)) { 234 DRM_ERROR("failed to remap pfn range.\n"); 235 return -EAGAIN; 236 } 237 238 return 0; 239} 240 241static const struct file_operations exynos_drm_gem_fops = { 242 .mmap = exynos_drm_gem_mmap_buffer, 243}; 244 245int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data, 246 struct drm_file *file_priv) 247{ 248 struct drm_exynos_gem_mmap *args = data; 249 struct drm_gem_object *obj; 250 unsigned int addr; 251 252 DRM_DEBUG_KMS("%s\n", __FILE__); 253 254 if (!(dev->driver->driver_features & DRIVER_GEM)) { 255 DRM_ERROR("does not support GEM.\n"); 256 return -ENODEV; 257 } 258 259 obj = drm_gem_object_lookup(dev, file_priv, args->handle); 260 if (!obj) { 261 DRM_ERROR("failed to lookup gem object.\n"); 262 return -EINVAL; 263 } 264 265 obj->filp->f_op = &exynos_drm_gem_fops; 266 obj->filp->private_data = obj; 267 268 down_write(¤t->mm->mmap_sem); 269 addr = do_mmap(obj->filp, 0, args->size, 270 PROT_READ | PROT_WRITE, MAP_SHARED, 0); 271 up_write(¤t->mm->mmap_sem); 272 273 drm_gem_object_unreference_unlocked(obj); 274 275 if (IS_ERR((void *)addr)) 276 return PTR_ERR((void *)addr); 277 278 args->mapped = addr; 279 280 DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped); 281 282 return 0; 283} 284 285int exynos_drm_gem_init_object(struct drm_gem_object *obj) 286{ 287 DRM_DEBUG_KMS("%s\n", __FILE__); 288 289 return 0; 290} 291 292void exynos_drm_gem_free_object(struct drm_gem_object *obj) 293{ 294 DRM_DEBUG_KMS("%s\n", __FILE__); 295 296 exynos_drm_gem_destroy(to_exynos_gem_obj(obj)); 297} 298 299int exynos_drm_gem_dumb_create(struct drm_file *file_priv, 300 struct drm_device *dev, 301 struct drm_mode_create_dumb *args) 302{ 303 struct exynos_drm_gem_obj *exynos_gem_obj; 304 int ret; 305 306 DRM_DEBUG_KMS("%s\n", __FILE__); 307 308 /* 309 * alocate memory to be used for framebuffer. 310 * - this callback would be called by user application 311 * with DRM_IOCTL_MODE_CREATE_DUMB command. 312 */ 313 314 args->pitch = args->width * args->bpp >> 3; 315 args->size = args->pitch * args->height; 316 317 exynos_gem_obj = exynos_drm_gem_create(dev, args->size); 318 if (IS_ERR(exynos_gem_obj)) 319 return PTR_ERR(exynos_gem_obj); 320 321 ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv, 322 &args->handle); 323 if (ret) { 324 exynos_drm_gem_destroy(exynos_gem_obj); 325 return ret; 326 } 327 328 return 0; 329} 330 331int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv, 332 struct drm_device *dev, uint32_t handle, 333 uint64_t *offset) 334{ 335 struct exynos_drm_gem_obj *exynos_gem_obj; 336 struct drm_gem_object *obj; 337 int ret = 0; 338 339 DRM_DEBUG_KMS("%s\n", __FILE__); 340 341 mutex_lock(&dev->struct_mutex); 342 343 /* 344 * get offset of memory allocated for drm framebuffer. 345 * - this callback would be called by user application 346 * with DRM_IOCTL_MODE_MAP_DUMB command. 347 */ 348 349 obj = drm_gem_object_lookup(dev, file_priv, handle); 350 if (!obj) { 351 DRM_ERROR("failed to lookup gem object.\n"); 352 ret = -EINVAL; 353 goto unlock; 354 } 355 356 exynos_gem_obj = to_exynos_gem_obj(obj); 357 358 if (!exynos_gem_obj->base.map_list.map) { 359 ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base); 360 if (ret) 361 goto out; 362 } 363 364 *offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT; 365 DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset); 366 367out: 368 drm_gem_object_unreference(obj); 369unlock: 370 mutex_unlock(&dev->struct_mutex); 371 return ret; 372} 373 374int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv, 375 struct drm_device *dev, 376 unsigned int handle) 377{ 378 int ret; 379 380 DRM_DEBUG_KMS("%s\n", __FILE__); 381 382 /* 383 * obj->refcount and obj->handle_count are decreased and 384 * if both them are 0 then exynos_drm_gem_free_object() 385 * would be called by callback to release resources. 386 */ 387 ret = drm_gem_handle_delete(file_priv, handle); 388 if (ret < 0) { 389 DRM_ERROR("failed to delete drm_gem_handle.\n"); 390 return ret; 391 } 392 393 return 0; 394} 395 396int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) 397{ 398 struct drm_gem_object *obj = vma->vm_private_data; 399 struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj); 400 struct drm_device *dev = obj->dev; 401 unsigned long pfn; 402 pgoff_t page_offset; 403 int ret; 404 405 page_offset = ((unsigned long)vmf->virtual_address - 406 vma->vm_start) >> PAGE_SHIFT; 407 408 mutex_lock(&dev->struct_mutex); 409 410 pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >> 411 PAGE_SHIFT) + page_offset; 412 413 ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); 414 415 mutex_unlock(&dev->struct_mutex); 416 417 return convert_to_vm_err_msg(ret); 418} 419 420int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) 421{ 422 int ret; 423 424 DRM_DEBUG_KMS("%s\n", __FILE__); 425 426 /* set vm_area_struct. */ 427 ret = drm_gem_mmap(filp, vma); 428 if (ret < 0) { 429 DRM_ERROR("failed to mmap.\n"); 430 return ret; 431 } 432 433 vma->vm_flags &= ~VM_PFNMAP; 434 vma->vm_flags |= VM_MIXEDMAP; 435 436 return ret; 437} 438 439MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>"); 440MODULE_DESCRIPTION("Samsung SoC DRM GEM Module"); 441MODULE_LICENSE("GPL"); 442