drm/radeon/kms/pm: add a proper pm profile init function for fusion
[pandora-kernel.git] / drivers / gpu / drm / drm_sman.c
1 /**************************************************************************
2  *
3  * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
17  * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
18  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20  * USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  *
27  **************************************************************************/
28 /*
29  * Simple memory manager interface that keeps track on allocate regions on a
30  * per "owner" basis. All regions associated with an "owner" can be released
31  * with a simple call. Typically if the "owner" exists. The owner is any
32  * "unsigned long" identifier. Can typically be a pointer to a file private
33  * struct or a context identifier.
34  *
35  * Authors:
36  * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
37  */
38
39 #include <linux/export.h>
40 #include "drm_sman.h"
41
42 struct drm_owner_item {
43         struct drm_hash_item owner_hash;
44         struct list_head sman_list;
45         struct list_head mem_blocks;
46 };
47
48 void drm_sman_takedown(struct drm_sman * sman)
49 {
50         drm_ht_remove(&sman->user_hash_tab);
51         drm_ht_remove(&sman->owner_hash_tab);
52         kfree(sman->mm);
53 }
54
55 EXPORT_SYMBOL(drm_sman_takedown);
56
57 int
58 drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
59               unsigned int user_order, unsigned int owner_order)
60 {
61         int ret = 0;
62
63         sman->mm = kcalloc(num_managers, sizeof(*sman->mm), GFP_KERNEL);
64         if (!sman->mm) {
65                 ret = -ENOMEM;
66                 goto out;
67         }
68         sman->num_managers = num_managers;
69         INIT_LIST_HEAD(&sman->owner_items);
70         ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
71         if (ret)
72                 goto out1;
73         ret = drm_ht_create(&sman->user_hash_tab, user_order);
74         if (!ret)
75                 goto out;
76
77         drm_ht_remove(&sman->owner_hash_tab);
78 out1:
79         kfree(sman->mm);
80 out:
81         return ret;
82 }
83
84 EXPORT_SYMBOL(drm_sman_init);
85
86 static void *drm_sman_mm_allocate(void *private, unsigned long size,
87                                   unsigned alignment)
88 {
89         struct drm_mm *mm = (struct drm_mm *) private;
90         struct drm_mm_node *tmp;
91
92         tmp = drm_mm_search_free(mm, size, alignment, 1);
93         if (!tmp) {
94                 return NULL;
95         }
96         tmp = drm_mm_get_block(tmp, size, alignment);
97         return tmp;
98 }
99
100 static void drm_sman_mm_free(void *private, void *ref)
101 {
102         struct drm_mm_node *node = (struct drm_mm_node *) ref;
103
104         drm_mm_put_block(node);
105 }
106
107 static void drm_sman_mm_destroy(void *private)
108 {
109         struct drm_mm *mm = (struct drm_mm *) private;
110         drm_mm_takedown(mm);
111         kfree(mm);
112 }
113
114 static unsigned long drm_sman_mm_offset(void *private, void *ref)
115 {
116         struct drm_mm_node *node = (struct drm_mm_node *) ref;
117         return node->start;
118 }
119
120 int
121 drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
122                    unsigned long start, unsigned long size)
123 {
124         struct drm_sman_mm *sman_mm;
125         struct drm_mm *mm;
126         int ret;
127
128         BUG_ON(manager >= sman->num_managers);
129
130         sman_mm = &sman->mm[manager];
131         mm = kzalloc(sizeof(*mm), GFP_KERNEL);
132         if (!mm) {
133                 return -ENOMEM;
134         }
135         sman_mm->private = mm;
136         ret = drm_mm_init(mm, start, size);
137
138         if (ret) {
139                 kfree(mm);
140                 return ret;
141         }
142
143         sman_mm->allocate = drm_sman_mm_allocate;
144         sman_mm->free = drm_sman_mm_free;
145         sman_mm->destroy = drm_sman_mm_destroy;
146         sman_mm->offset = drm_sman_mm_offset;
147
148         return 0;
149 }
150
151 EXPORT_SYMBOL(drm_sman_set_range);
152
153 int
154 drm_sman_set_manager(struct drm_sman * sman, unsigned int manager,
155                      struct drm_sman_mm * allocator)
156 {
157         BUG_ON(manager >= sman->num_managers);
158         sman->mm[manager] = *allocator;
159
160         return 0;
161 }
162 EXPORT_SYMBOL(drm_sman_set_manager);
163
164 static struct drm_owner_item *drm_sman_get_owner_item(struct drm_sman * sman,
165                                                  unsigned long owner)
166 {
167         int ret;
168         struct drm_hash_item *owner_hash_item;
169         struct drm_owner_item *owner_item;
170
171         ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item);
172         if (!ret) {
173                 return drm_hash_entry(owner_hash_item, struct drm_owner_item,
174                                       owner_hash);
175         }
176
177         owner_item = kzalloc(sizeof(*owner_item), GFP_KERNEL);
178         if (!owner_item)
179                 goto out;
180
181         INIT_LIST_HEAD(&owner_item->mem_blocks);
182         owner_item->owner_hash.key = owner;
183         if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
184                 goto out1;
185
186         list_add_tail(&owner_item->sman_list, &sman->owner_items);
187         return owner_item;
188
189 out1:
190         kfree(owner_item);
191 out:
192         return NULL;
193 }
194
195 struct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager,
196                                     unsigned long size, unsigned alignment,
197                                     unsigned long owner)
198 {
199         void *tmp;
200         struct drm_sman_mm *sman_mm;
201         struct drm_owner_item *owner_item;
202         struct drm_memblock_item *memblock;
203
204         BUG_ON(manager >= sman->num_managers);
205
206         sman_mm = &sman->mm[manager];
207         tmp = sman_mm->allocate(sman_mm->private, size, alignment);
208
209         if (!tmp) {
210                 return NULL;
211         }
212
213         memblock = kzalloc(sizeof(*memblock), GFP_KERNEL);
214
215         if (!memblock)
216                 goto out;
217
218         memblock->mm_info = tmp;
219         memblock->mm = sman_mm;
220         memblock->sman = sman;
221
222         if (drm_ht_just_insert_please
223             (&sman->user_hash_tab, &memblock->user_hash,
224              (unsigned long)memblock, 32, 0, 0))
225                 goto out1;
226
227         owner_item = drm_sman_get_owner_item(sman, owner);
228         if (!owner_item)
229                 goto out2;
230
231         list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
232
233         return memblock;
234
235 out2:
236         drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
237 out1:
238         kfree(memblock);
239 out:
240         sman_mm->free(sman_mm->private, tmp);
241
242         return NULL;
243 }
244
245 EXPORT_SYMBOL(drm_sman_alloc);
246
247 static void drm_sman_free(struct drm_memblock_item *item)
248 {
249         struct drm_sman *sman = item->sman;
250
251         list_del(&item->owner_list);
252         drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
253         item->mm->free(item->mm->private, item->mm_info);
254         kfree(item);
255 }
256
257 int drm_sman_free_key(struct drm_sman *sman, unsigned int key)
258 {
259         struct drm_hash_item *hash_item;
260         struct drm_memblock_item *memblock_item;
261
262         if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
263                 return -EINVAL;
264
265         memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item,
266                                        user_hash);
267         drm_sman_free(memblock_item);
268         return 0;
269 }
270
271 EXPORT_SYMBOL(drm_sman_free_key);
272
273 static void drm_sman_remove_owner(struct drm_sman *sman,
274                                   struct drm_owner_item *owner_item)
275 {
276         list_del(&owner_item->sman_list);
277         drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
278         kfree(owner_item);
279 }
280
281 int drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner)
282 {
283
284         struct drm_hash_item *hash_item;
285         struct drm_owner_item *owner_item;
286
287         if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
288                 return -1;
289         }
290
291         owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
292         if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
293                 drm_sman_remove_owner(sman, owner_item);
294                 return -1;
295         }
296
297         return 0;
298 }
299
300 EXPORT_SYMBOL(drm_sman_owner_clean);
301
302 static void drm_sman_do_owner_cleanup(struct drm_sman *sman,
303                                       struct drm_owner_item *owner_item)
304 {
305         struct drm_memblock_item *entry, *next;
306
307         list_for_each_entry_safe(entry, next, &owner_item->mem_blocks,
308                                  owner_list) {
309                 drm_sman_free(entry);
310         }
311         drm_sman_remove_owner(sman, owner_item);
312 }
313
314 void drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner)
315 {
316
317         struct drm_hash_item *hash_item;
318         struct drm_owner_item *owner_item;
319
320         if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
321
322                 return;
323         }
324
325         owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
326         drm_sman_do_owner_cleanup(sman, owner_item);
327 }
328
329 EXPORT_SYMBOL(drm_sman_owner_cleanup);
330
331 void drm_sman_cleanup(struct drm_sman *sman)
332 {
333         struct drm_owner_item *entry, *next;
334         unsigned int i;
335         struct drm_sman_mm *sman_mm;
336
337         list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
338                 drm_sman_do_owner_cleanup(sman, entry);
339         }
340         if (sman->mm) {
341                 for (i = 0; i < sman->num_managers; ++i) {
342                         sman_mm = &sman->mm[i];
343                         if (sman_mm->private) {
344                                 sman_mm->destroy(sman_mm->private);
345                                 sman_mm->private = NULL;
346                         }
347                 }
348         }
349 }
350
351 EXPORT_SYMBOL(drm_sman_cleanup);