Release mapping resources when deleting a GEM object
[kernel.git] / drivers / mfd / glamo / glamo-buffer.c
1 /*
2  * SMedia Glamo 336x/337x memory management
3  *
4  * Copyright (c) 2009 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *
20  * Memory mapping functions based on i915_gem.c, to which the following
21  * notice applies:
22  *
23  * Copyright © 2008 Intel Corporation
24  *
25  * Permission is hereby granted, free of charge, to any person obtaining a
26  * copy of this software and associated documentation files (the "Software"),
27  * to deal in the Software without restriction, including without limitation
28  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
29  * and/or sell copies of the Software, and to permit persons to whom the
30  * Software is furnished to do so, subject to the following conditions:
31  *
32  * The above copyright notice and this permission notice (including the next
33  * paragraph) shall be included in all copies or substantial portions of the
34  * Software.
35  *
36  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
37  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
38  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
39  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
40  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
41  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
42  * IN THE SOFTWARE.
43  *
44  * Authors:
45  *    Eric Anholt <eric@anholt.net>
46  */
47
48
49 #include <drm/drmP.h>
50 #include <drm/glamo_drm.h>
51
52 #include "glamo-drm-private.h"
53 #include "glamo-cmdq.h" /* For glamo_cmdq_blank() */
54
55
56 struct drm_gem_object *glamo_gem_object_alloc(struct drm_device *dev, int size,
57                                               int alignment)
58 {
59         struct drm_gem_object *obj;
60         struct glamodrm_handle *gdrm;
61         struct drm_glamo_gem_object *gobj;
62
63         gdrm = dev->dev_private;
64
65         size = roundup(size, PAGE_SIZE);
66
67         obj = drm_gem_object_alloc(dev, size);
68         if (obj == NULL) return NULL;
69
70         /* See glamodrm_gem_init_object() below */
71         gobj = obj->driver_private;
72
73         /* Allocate memory for this object in VRAM */
74         gobj->block = drm_mm_search_free(gdrm->mmgr, size, alignment, 1);
75         if (!gobj->block) {
76                 goto fail;
77         }
78         gobj->block = drm_mm_get_block(gobj->block, size, alignment);
79         if (!gobj->block) {
80                 goto fail;
81         }
82
83         /* Arrange for the contents to be set to zero */
84         glamo_cmdq_blank(gdrm, obj);
85
86         return obj;
87
88 fail:
89         mutex_lock(&dev->struct_mutex);
90         drm_gem_object_unreference(obj);
91         mutex_unlock(&dev->struct_mutex);
92         printk(KERN_INFO "[glamo-drm] Failed to allocate object\n");
93
94         return NULL;
95 }
96
97
98 int glamo_ioctl_gem_create(struct drm_device *dev, void *data,
99                            struct drm_file *file_priv)
100 {
101         struct drm_glamo_gem_create *args = data;
102         struct drm_gem_object *obj;
103         int handle, ret;
104
105         /* Create an object */
106         obj = glamo_gem_object_alloc(dev, args->size, args->alignment);
107         if ( obj == NULL ) return -ENOMEM;
108
109         /* Create a handle for it */
110         ret = drm_gem_handle_create(file_priv, obj, &handle);
111         mutex_lock(&dev->struct_mutex);
112         drm_gem_object_handle_unreference(obj);
113         mutex_unlock(&dev->struct_mutex);
114         if (ret) goto fail;
115
116         /* Return */
117         args->handle = handle;
118         return 0;
119
120 fail:
121         mutex_lock(&dev->struct_mutex);
122         drm_gem_object_unreference(obj);
123         mutex_unlock(&dev->struct_mutex);
124         printk(KERN_INFO "[glamo-drm] Failed to allocate object\n");
125         return ret;
126 }
127
128
129 int glamodrm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
130 {
131         struct drm_gem_object *obj = vma->vm_private_data;
132         struct drm_device *dev = obj->dev;
133         struct drm_glamo_gem_object *gobj = obj->driver_private;
134         struct glamodrm_handle *gdrm = dev->dev_private;
135         pgoff_t page_offset;
136         unsigned long pfn;
137         int ret = 0;
138
139         /* We don't use vmf->pgoff since that has the fake offset */
140         page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
141                        PAGE_SHIFT;
142
143         mutex_lock(&dev->struct_mutex);
144         pfn = ((gdrm->vram->start + gobj->block->start) >> PAGE_SHIFT)
145                + page_offset;
146         ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
147         mutex_unlock(&dev->struct_mutex);
148
149         switch (ret) {
150         case -ENOMEM:
151         case -EAGAIN:
152                 return VM_FAULT_OOM;
153         case -EFAULT:
154         case -EBUSY:
155                 DRM_ERROR("can't insert pfn??  fault or busy...\n");
156                 return VM_FAULT_SIGBUS;
157         default:
158                 return VM_FAULT_NOPAGE;
159         }
160 }
161
162
163 static int glamo_gem_create_mmap_offset(struct drm_gem_object *obj)
164 {
165         struct drm_device *dev = obj->dev;
166         struct drm_gem_mm *mm = dev->mm_private;
167         struct drm_glamo_gem_object *gobj = obj->driver_private;
168         struct drm_map_list *list;
169         struct drm_map *map;
170         int ret = 0;
171
172         /* Set the object up for mmap'ing */
173         list = &obj->map_list;
174         list->map = drm_calloc(1, sizeof(struct drm_map_list), DRM_MEM_DRIVER);
175         if (!list->map)
176                 return -ENOMEM;
177
178         map = list->map;
179         map->type = _DRM_GEM;
180         map->size = obj->size;
181         map->handle = obj;
182
183         /* Get a DRM GEM mmap offset allocated... */
184         list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
185                                                     obj->size / PAGE_SIZE, 0, 0);
186         if (!list->file_offset_node) {
187                 DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
188                 ret = -ENOMEM;
189                 goto out_free_list;
190         }
191
192         list->file_offset_node = drm_mm_get_block(list->file_offset_node,
193                                                   obj->size / PAGE_SIZE, 0);
194         if (!list->file_offset_node) {
195                 ret = -ENOMEM;
196                 goto out_free_list;
197         }
198
199         list->hash.key = list->file_offset_node->start;
200         if (drm_ht_insert_item(&mm->offset_hash, &list->hash)) {
201                 DRM_ERROR("failed to add to map hash\n");
202                 goto out_free_mm;
203         }
204
205         /* By now we should be all set, any drm_mmap request on the offset
206          * below will get to our mmap & fault handler */
207         gobj->mmap_offset = ((uint64_t) list->hash.key) << PAGE_SHIFT;
208
209         return 0;
210
211 out_free_mm:
212         drm_mm_put_block(list->file_offset_node);
213 out_free_list:
214         drm_free(list->map, sizeof(struct drm_map_list), DRM_MEM_DRIVER);
215
216         return ret;
217 }
218
219
220 int glamo_ioctl_gem_mmap(struct drm_device *dev, void *data,
221                          struct drm_file *file_priv)
222 {
223         struct drm_glamo_gem_mmap *args = data;
224         struct drm_gem_object *obj;
225         struct drm_glamo_gem_object *gobj;
226         int ret;
227
228         obj = drm_gem_object_lookup(dev, file_priv, args->handle);
229         if (obj == NULL)
230                 return -EBADF;
231
232         mutex_lock(&dev->struct_mutex);
233
234         gobj = obj->driver_private;
235         if (!gobj->mmap_offset) {
236                 ret = glamo_gem_create_mmap_offset(obj);
237                 if (ret) {
238                         mutex_unlock(&dev->struct_mutex);
239                         return ret;
240                 }
241         }
242
243         args->offset = gobj->mmap_offset;
244
245         drm_gem_object_unreference(obj);
246         mutex_unlock(&dev->struct_mutex);
247
248         return 0;
249 }
250
251
252 int glamo_ioctl_gem_pin(struct drm_device *dev, void *data,
253                         struct drm_file *file_priv)
254 {
255         printk(KERN_INFO "glamo_ioctl_gem_pin\n");
256         return 0;
257 }
258
259
260 int glamo_ioctl_gem_unpin(struct drm_device *dev, void *data,
261                           struct drm_file *file_priv)
262 {
263         printk(KERN_INFO "glamo_ioctl_gem_unpin\n");
264         return 0;
265 }
266
267
268 int glamo_ioctl_gem_pread(struct drm_device *dev, void *data,
269                           struct drm_file *file_priv)
270 {
271         printk(KERN_INFO "glamo_ioctl_gem_pread\n");
272         return 0;
273 }
274
275
276 int glamo_ioctl_gem_pwrite(struct drm_device *dev, void *data,
277                            struct drm_file *file_priv)
278 {
279         printk(KERN_INFO "glamo_ioctl_gem_pwrite\n");
280         return 0;
281 }
282
283
284 int glamodrm_gem_init_object(struct drm_gem_object *obj)
285 {
286         struct drm_glamo_gem_object *gobj;
287
288         /* Allocate a private structure */
289         gobj = drm_calloc(1, sizeof(*gobj), DRM_MEM_DRIVER);
290         if (!gobj) return -ENOMEM;
291
292         obj->driver_private = gobj;
293         gobj->obj = obj;
294
295         return 0;
296 }
297
298
299 void glamodrm_gem_free_object(struct drm_gem_object *obj)
300 {
301         struct drm_glamo_gem_object *gobj;
302         struct drm_map_list *list;
303         struct drm_device *dev;
304         struct drm_gem_mm *mm;
305         struct drm_map *map;
306
307         dev = obj->dev;
308         mm = dev->mm_private;
309         gobj = obj->driver_private;
310
311         /* Free the VRAM */
312         drm_mm_put_block(gobj->block);
313
314         /* Release mappings */
315         list = &obj->map_list;
316         drm_ht_remove_item(&mm->offset_hash, &list->hash);
317         if (list->file_offset_node) {
318                 drm_mm_put_block(list->file_offset_node);
319                 list->file_offset_node = NULL;
320         }
321         map = list->map;
322         if (map) {
323                 drm_free(map, sizeof(*map), DRM_MEM_DRIVER);
324                 list->map = NULL;
325         }
326
327         /* Free the private structure */
328         drm_free(obj->driver_private, 1, DRM_MEM_DRIVER);
329 }
330
331
332 /* Memory management initialisation */
333 int glamo_buffer_init(struct glamodrm_handle *gdrm)
334 {
335         gdrm->mmgr = drm_calloc(1, sizeof(struct drm_mm), DRM_MEM_DRIVER);
336         drm_mm_init(gdrm->mmgr, 0, gdrm->vram_size);
337         return 0;
338 }
339
340
341 /* Memory management finalisation */
342 int glamo_buffer_final(struct glamodrm_handle *gdrm)
343 {
344         drm_mm_takedown(gdrm->mmgr);
345         drm_free(gdrm->mmgr , 1, DRM_MEM_DRIVER);
346         return 0;
347 }