Merge tag 'rpmsg-v4.19' of git://github.com/andersson/remoteproc
[linux-2.6-microblaze.git] / drivers / gpu / drm / vkms / vkms_gem.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License as published by
5  * the Free Software Foundation; either version 2 of the License, or
6  * (at your option) any later version.
7  */
8
9 #include <linux/shmem_fs.h>
10
11 #include "vkms_drv.h"
12
13 static struct vkms_gem_object *__vkms_gem_create(struct drm_device *dev,
14                                                  u64 size)
15 {
16         struct vkms_gem_object *obj;
17         int ret;
18
19         obj = kzalloc(sizeof(*obj), GFP_KERNEL);
20         if (!obj)
21                 return ERR_PTR(-ENOMEM);
22
23         size = roundup(size, PAGE_SIZE);
24         ret = drm_gem_object_init(dev, &obj->gem, size);
25         if (ret) {
26                 kfree(obj);
27                 return ERR_PTR(ret);
28         }
29
30         mutex_init(&obj->pages_lock);
31
32         return obj;
33 }
34
35 void vkms_gem_free_object(struct drm_gem_object *obj)
36 {
37         struct vkms_gem_object *gem = container_of(obj, struct vkms_gem_object,
38                                                    gem);
39
40         kvfree(gem->pages);
41         mutex_destroy(&gem->pages_lock);
42         drm_gem_object_release(obj);
43         kfree(gem);
44 }
45
46 int vkms_gem_fault(struct vm_fault *vmf)
47 {
48         struct vm_area_struct *vma = vmf->vma;
49         struct vkms_gem_object *obj = vma->vm_private_data;
50         unsigned long vaddr = vmf->address;
51         pgoff_t page_offset;
52         loff_t num_pages;
53         int ret;
54
55         page_offset = (vaddr - vma->vm_start) >> PAGE_SHIFT;
56         num_pages = DIV_ROUND_UP(obj->gem.size, PAGE_SIZE);
57
58         if (page_offset > num_pages)
59                 return VM_FAULT_SIGBUS;
60
61         ret = -ENOENT;
62         mutex_lock(&obj->pages_lock);
63         if (obj->pages) {
64                 get_page(obj->pages[page_offset]);
65                 vmf->page = obj->pages[page_offset];
66                 ret = 0;
67         }
68         mutex_unlock(&obj->pages_lock);
69         if (ret) {
70                 struct page *page;
71                 struct address_space *mapping;
72
73                 mapping = file_inode(obj->gem.filp)->i_mapping;
74                 page = shmem_read_mapping_page(mapping, page_offset);
75
76                 if (!IS_ERR(page)) {
77                         vmf->page = page;
78                         ret = 0;
79                 } else {
80                         switch (PTR_ERR(page)) {
81                         case -ENOSPC:
82                         case -ENOMEM:
83                                 ret = VM_FAULT_OOM;
84                                 break;
85                         case -EBUSY:
86                                 ret = VM_FAULT_RETRY;
87                                 break;
88                         case -EFAULT:
89                         case -EINVAL:
90                                 ret = VM_FAULT_SIGBUS;
91                                 break;
92                         default:
93                                 WARN_ON(PTR_ERR(page));
94                                 ret = VM_FAULT_SIGBUS;
95                                 break;
96                         }
97                 }
98         }
99         return ret;
100 }
101
102 struct drm_gem_object *vkms_gem_create(struct drm_device *dev,
103                                        struct drm_file *file,
104                                        u32 *handle,
105                                        u64 size)
106 {
107         struct vkms_gem_object *obj;
108         int ret;
109
110         if (!file || !dev || !handle)
111                 return ERR_PTR(-EINVAL);
112
113         obj = __vkms_gem_create(dev, size);
114         if (IS_ERR(obj))
115                 return ERR_CAST(obj);
116
117         ret = drm_gem_handle_create(file, &obj->gem, handle);
118         drm_gem_object_put_unlocked(&obj->gem);
119         if (ret) {
120                 drm_gem_object_release(&obj->gem);
121                 kfree(obj);
122                 return ERR_PTR(ret);
123         }
124
125         return &obj->gem;
126 }
127
128 int vkms_dumb_create(struct drm_file *file, struct drm_device *dev,
129                      struct drm_mode_create_dumb *args)
130 {
131         struct drm_gem_object *gem_obj;
132         u64 pitch, size;
133
134         if (!args || !dev || !file)
135                 return -EINVAL;
136
137         pitch = args->width * DIV_ROUND_UP(args->bpp, 8);
138         size = pitch * args->height;
139
140         if (!size)
141                 return -EINVAL;
142
143         gem_obj = vkms_gem_create(dev, file, &args->handle, size);
144         if (IS_ERR(gem_obj))
145                 return PTR_ERR(gem_obj);
146
147         args->size = gem_obj->size;
148         args->pitch = pitch;
149
150         DRM_DEBUG_DRIVER("Created object of size %lld\n", size);
151
152         return 0;
153 }
154
155 int vkms_dumb_map(struct drm_file *file, struct drm_device *dev,
156                   u32 handle, u64 *offset)
157 {
158         struct drm_gem_object *obj;
159         int ret;
160
161         obj = drm_gem_object_lookup(file, handle);
162         if (!obj)
163                 return -ENOENT;
164
165         if (!obj->filp) {
166                 ret = -EINVAL;
167                 goto unref;
168         }
169
170         ret = drm_gem_create_mmap_offset(obj);
171         if (ret)
172                 goto unref;
173
174         *offset = drm_vma_node_offset_addr(&obj->vma_node);
175 unref:
176         drm_gem_object_put_unlocked(obj);
177
178         return ret;
179 }