diff options
Diffstat (limited to 'drivers/gpu/drm/drm_gem.c')
-rw-r--r-- | drivers/gpu/drm/drm_gem.c | 109 |
1 files changed, 109 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index ccd1afdede02..b3939de6affd 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c | |||
@@ -64,6 +64,13 @@ | |||
64 | * up at a later date, and as our interface with shmfs for memory allocation. | 64 | * up at a later date, and as our interface with shmfs for memory allocation. |
65 | */ | 65 | */ |
66 | 66 | ||
67 | /* | ||
68 | * We make up offsets for buffer objects so we can recognize them at | ||
69 | * mmap time. | ||
70 | */ | ||
71 | #define DRM_FILE_PAGE_OFFSET_START ((0xFFFFFFFFUL >> PAGE_SHIFT) + 1) | ||
72 | #define DRM_FILE_PAGE_OFFSET_SIZE ((0xFFFFFFFFUL >> PAGE_SHIFT) * 16) | ||
73 | |||
67 | /** | 74 | /** |
68 | * Initialize the GEM device fields | 75 | * Initialize the GEM device fields |
69 | */ | 76 | */ |
@@ -71,6 +78,8 @@ | |||
71 | int | 78 | int |
72 | drm_gem_init(struct drm_device *dev) | 79 | drm_gem_init(struct drm_device *dev) |
73 | { | 80 | { |
81 | struct drm_gem_mm *mm; | ||
82 | |||
74 | spin_lock_init(&dev->object_name_lock); | 83 | spin_lock_init(&dev->object_name_lock); |
75 | idr_init(&dev->object_name_idr); | 84 | idr_init(&dev->object_name_idr); |
76 | atomic_set(&dev->object_count, 0); | 85 | atomic_set(&dev->object_count, 0); |
@@ -79,9 +88,41 @@ drm_gem_init(struct drm_device *dev) | |||
79 | atomic_set(&dev->pin_memory, 0); | 88 | atomic_set(&dev->pin_memory, 0); |
80 | atomic_set(&dev->gtt_count, 0); | 89 | atomic_set(&dev->gtt_count, 0); |
81 | atomic_set(&dev->gtt_memory, 0); | 90 | atomic_set(&dev->gtt_memory, 0); |
91 | |||
92 | mm = drm_calloc(1, sizeof(struct drm_gem_mm), DRM_MEM_MM); | ||
93 | if (!mm) { | ||
94 | DRM_ERROR("out of memory\n"); | ||
95 | return -ENOMEM; | ||
96 | } | ||
97 | |||
98 | dev->mm_private = mm; | ||
99 | |||
100 | if (drm_ht_create(&mm->offset_hash, 19)) { | ||
101 | drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); | ||
102 | return -ENOMEM; | ||
103 | } | ||
104 | |||
105 | if (drm_mm_init(&mm->offset_manager, DRM_FILE_PAGE_OFFSET_START, | ||
106 | DRM_FILE_PAGE_OFFSET_SIZE)) { | ||
107 | drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); | ||
108 | drm_ht_remove(&mm->offset_hash); | ||
109 | return -ENOMEM; | ||
110 | } | ||
111 | |||
82 | return 0; | 112 | return 0; |
83 | } | 113 | } |
84 | 114 | ||
115 | void | ||
116 | drm_gem_destroy(struct drm_device *dev) | ||
117 | { | ||
118 | struct drm_gem_mm *mm = dev->mm_private; | ||
119 | |||
120 | drm_mm_takedown(&mm->offset_manager); | ||
121 | drm_ht_remove(&mm->offset_hash); | ||
122 | drm_free(mm, sizeof(struct drm_gem_mm), DRM_MEM_MM); | ||
123 | dev->mm_private = NULL; | ||
124 | } | ||
125 | |||
85 | /** | 126 | /** |
86 | * Allocate a GEM object of the specified size with shmfs backing store | 127 | * Allocate a GEM object of the specified size with shmfs backing store |
87 | */ | 128 | */ |
@@ -419,3 +460,71 @@ drm_gem_object_handle_free(struct kref *kref) | |||
419 | } | 460 | } |
420 | EXPORT_SYMBOL(drm_gem_object_handle_free); | 461 | EXPORT_SYMBOL(drm_gem_object_handle_free); |
421 | 462 | ||
463 | /** | ||
464 | * drm_gem_mmap - memory map routine for GEM objects | ||
465 | * @filp: DRM file pointer | ||
466 | * @vma: VMA for the area to be mapped | ||
467 | * | ||
468 | * If a driver supports GEM object mapping, mmap calls on the DRM file | ||
469 | * descriptor will end up here. | ||
470 | * | ||
471 | * If we find the object based on the offset passed in (vma->vm_pgoff will | ||
472 | * contain the fake offset we created when the GTT map ioctl was called on | ||
473 | * the object), we set up the driver fault handler so that any accesses | ||
474 | * to the object can be trapped, to perform migration, GTT binding, surface | ||
475 | * register allocation, or performance monitoring. | ||
476 | */ | ||
477 | int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma) | ||
478 | { | ||
479 | struct drm_file *priv = filp->private_data; | ||
480 | struct drm_device *dev = priv->minor->dev; | ||
481 | struct drm_gem_mm *mm = dev->mm_private; | ||
482 | struct drm_map *map = NULL; | ||
483 | struct drm_gem_object *obj; | ||
484 | struct drm_hash_item *hash; | ||
485 | unsigned long prot; | ||
486 | int ret = 0; | ||
487 | |||
488 | mutex_lock(&dev->struct_mutex); | ||
489 | |||
490 | if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) { | ||
491 | mutex_unlock(&dev->struct_mutex); | ||
492 | return drm_mmap(filp, vma); | ||
493 | } | ||
494 | |||
495 | map = drm_hash_entry(hash, struct drm_map_list, hash)->map; | ||
496 | if (!map || | ||
497 | ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) { | ||
498 | ret = -EPERM; | ||
499 | goto out_unlock; | ||
500 | } | ||
501 | |||
502 | /* Check for valid size. */ | ||
503 | if (map->size < vma->vm_end - vma->vm_start) { | ||
504 | ret = -EINVAL; | ||
505 | goto out_unlock; | ||
506 | } | ||
507 | |||
508 | obj = map->handle; | ||
509 | if (!obj->dev->driver->gem_vm_ops) { | ||
510 | ret = -EINVAL; | ||
511 | goto out_unlock; | ||
512 | } | ||
513 | |||
514 | vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND; | ||
515 | vma->vm_ops = obj->dev->driver->gem_vm_ops; | ||
516 | vma->vm_private_data = map->handle; | ||
517 | /* FIXME: use pgprot_writecombine when available */ | ||
518 | prot = pgprot_val(vma->vm_page_prot); | ||
519 | prot |= _PAGE_CACHE_WC; | ||
520 | vma->vm_page_prot = __pgprot(prot); | ||
521 | |||
522 | vma->vm_file = filp; /* Needed for drm_vm_open() */ | ||
523 | drm_vm_open_locked(vma); | ||
524 | |||
525 | out_unlock: | ||
526 | mutex_unlock(&dev->struct_mutex); | ||
527 | |||
528 | return ret; | ||
529 | } | ||
530 | EXPORT_SYMBOL(drm_gem_mmap); | ||