diff options
Diffstat (limited to 'drivers/char/drm/via_mm.c')
-rw-r--r-- | drivers/char/drm/via_mm.c | 375 |
1 files changed, 111 insertions, 264 deletions
diff --git a/drivers/char/drm/via_mm.c b/drivers/char/drm/via_mm.c index 33e0cb12e4c3..9d9237ef9448 100644 --- a/drivers/char/drm/via_mm.c +++ b/drivers/char/drm/via_mm.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved. | 2 | * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA. |
3 | * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
5 | * Permission is hereby granted, free of charge, to any person obtaining a | 5 | * Permission is hereby granted, free of charge, to any person obtaining a |
6 | * copy of this software and associated documentation files (the "Software"), | 6 | * copy of this software and associated documentation files (the "Software"), |
@@ -16,347 +16,194 @@ | |||
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL | 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL |
19 | * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR | 19 | * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | 20 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | 21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
22 | * DEALINGS IN THE SOFTWARE. | 22 | * DEALINGS IN THE SOFTWARE. |
23 | */ | 23 | */ |
24 | /* | ||
25 | * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com> | ||
26 | */ | ||
27 | |||
24 | #include "drmP.h" | 28 | #include "drmP.h" |
25 | #include "via_drm.h" | 29 | #include "via_drm.h" |
26 | #include "via_drv.h" | 30 | #include "via_drv.h" |
27 | #include "via_ds.h" | 31 | #include "drm_sman.h" |
28 | #include "via_mm.h" | ||
29 | |||
30 | #define MAX_CONTEXT 100 | ||
31 | |||
32 | typedef struct { | ||
33 | int used; | ||
34 | int context; | ||
35 | set_t *sets[2]; /* 0 for frame buffer, 1 for AGP , 2 for System */ | ||
36 | } via_context_t; | ||
37 | |||
38 | static via_context_t global_ppriv[MAX_CONTEXT]; | ||
39 | 32 | ||
40 | static int via_agp_alloc(drm_via_mem_t * mem); | 33 | #define VIA_MM_ALIGN_SHIFT 4 |
41 | static int via_agp_free(drm_via_mem_t * mem); | 34 | #define VIA_MM_ALIGN_MASK ( (1 << VIA_MM_ALIGN_SHIFT) - 1) |
42 | static int via_fb_alloc(drm_via_mem_t * mem); | ||
43 | static int via_fb_free(drm_via_mem_t * mem); | ||
44 | |||
45 | static int add_alloc_set(int context, int type, unsigned long val) | ||
46 | { | ||
47 | int i, retval = 0; | ||
48 | |||
49 | for (i = 0; i < MAX_CONTEXT; i++) { | ||
50 | if (global_ppriv[i].used && global_ppriv[i].context == context) { | ||
51 | retval = via_setAdd(global_ppriv[i].sets[type], val); | ||
52 | break; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | return retval; | ||
57 | } | ||
58 | |||
59 | static int del_alloc_set(int context, int type, unsigned long val) | ||
60 | { | ||
61 | int i, retval = 0; | ||
62 | |||
63 | for (i = 0; i < MAX_CONTEXT; i++) | ||
64 | if (global_ppriv[i].used && global_ppriv[i].context == context) { | ||
65 | retval = via_setDel(global_ppriv[i].sets[type], val); | ||
66 | break; | ||
67 | } | ||
68 | |||
69 | return retval; | ||
70 | } | ||
71 | |||
72 | /* agp memory management */ | ||
73 | static memHeap_t *AgpHeap = NULL; | ||
74 | 35 | ||
75 | int via_agp_init(DRM_IOCTL_ARGS) | 36 | int via_agp_init(DRM_IOCTL_ARGS) |
76 | { | 37 | { |
38 | DRM_DEVICE; | ||
77 | drm_via_agp_t agp; | 39 | drm_via_agp_t agp; |
40 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | ||
41 | int ret; | ||
78 | 42 | ||
79 | DRM_COPY_FROM_USER_IOCTL(agp, (drm_via_agp_t __user *) data, | 43 | DRM_COPY_FROM_USER_IOCTL(agp, (drm_via_agp_t __user *) data, |
80 | sizeof(agp)); | 44 | sizeof(agp)); |
45 | mutex_lock(&dev->struct_mutex); | ||
46 | ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_AGP, 0, | ||
47 | agp.size >> VIA_MM_ALIGN_SHIFT); | ||
48 | |||
49 | if (ret) { | ||
50 | DRM_ERROR("AGP memory manager initialisation error\n"); | ||
51 | mutex_unlock(&dev->struct_mutex); | ||
52 | return ret; | ||
53 | } | ||
81 | 54 | ||
82 | AgpHeap = via_mmInit(agp.offset, agp.size); | 55 | dev_priv->agp_initialized = TRUE; |
83 | 56 | dev_priv->agp_offset = agp.offset; | |
84 | DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)agp.offset, | 57 | mutex_unlock(&dev->struct_mutex); |
85 | (unsigned long)agp.size); | ||
86 | 58 | ||
59 | DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size); | ||
87 | return 0; | 60 | return 0; |
88 | } | 61 | } |
89 | 62 | ||
90 | /* fb memory management */ | ||
91 | static memHeap_t *FBHeap = NULL; | ||
92 | |||
93 | int via_fb_init(DRM_IOCTL_ARGS) | 63 | int via_fb_init(DRM_IOCTL_ARGS) |
94 | { | 64 | { |
65 | DRM_DEVICE; | ||
95 | drm_via_fb_t fb; | 66 | drm_via_fb_t fb; |
67 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | ||
68 | int ret; | ||
96 | 69 | ||
97 | DRM_COPY_FROM_USER_IOCTL(fb, (drm_via_fb_t __user *) data, sizeof(fb)); | 70 | DRM_COPY_FROM_USER_IOCTL(fb, (drm_via_fb_t __user *) data, sizeof(fb)); |
98 | 71 | ||
99 | FBHeap = via_mmInit(fb.offset, fb.size); | 72 | mutex_lock(&dev->struct_mutex); |
73 | ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_VIDEO, 0, | ||
74 | fb.size >> VIA_MM_ALIGN_SHIFT); | ||
100 | 75 | ||
101 | DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)fb.offset, | 76 | if (ret) { |
102 | (unsigned long)fb.size); | 77 | DRM_ERROR("VRAM memory manager initialisation error\n"); |
78 | mutex_unlock(&dev->struct_mutex); | ||
79 | return ret; | ||
80 | } | ||
103 | 81 | ||
104 | return 0; | 82 | dev_priv->vram_initialized = TRUE; |
105 | } | 83 | dev_priv->vram_offset = fb.offset; |
106 | 84 | ||
107 | int via_init_context(struct drm_device *dev, int context) | 85 | mutex_unlock(&dev->struct_mutex); |
108 | { | 86 | DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size); |
109 | int i; | 87 | |
110 | 88 | return 0; | |
111 | for (i = 0; i < MAX_CONTEXT; i++) | ||
112 | if (global_ppriv[i].used && | ||
113 | (global_ppriv[i].context == context)) | ||
114 | break; | ||
115 | |||
116 | if (i >= MAX_CONTEXT) { | ||
117 | for (i = 0; i < MAX_CONTEXT; i++) { | ||
118 | if (!global_ppriv[i].used) { | ||
119 | global_ppriv[i].context = context; | ||
120 | global_ppriv[i].used = 1; | ||
121 | global_ppriv[i].sets[0] = via_setInit(); | ||
122 | global_ppriv[i].sets[1] = via_setInit(); | ||
123 | DRM_DEBUG("init allocation set, socket=%d," | ||
124 | " context = %d\n", i, context); | ||
125 | break; | ||
126 | } | ||
127 | } | ||
128 | |||
129 | if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) || | ||
130 | (global_ppriv[i].sets[1] == NULL)) { | ||
131 | return 0; | ||
132 | } | ||
133 | } | ||
134 | 89 | ||
135 | return 1; | ||
136 | } | 90 | } |
137 | 91 | ||
138 | int via_final_context(struct drm_device *dev, int context) | 92 | int via_final_context(struct drm_device *dev, int context) |
139 | { | 93 | { |
140 | int i; | ||
141 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | 94 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; |
142 | 95 | ||
143 | for (i = 0; i < MAX_CONTEXT; i++) | ||
144 | if (global_ppriv[i].used && | ||
145 | (global_ppriv[i].context == context)) | ||
146 | break; | ||
147 | |||
148 | if (i < MAX_CONTEXT) { | ||
149 | set_t *set; | ||
150 | ITEM_TYPE item; | ||
151 | int retval; | ||
152 | |||
153 | DRM_DEBUG("find socket %d, context = %d\n", i, context); | ||
154 | |||
155 | /* Video Memory */ | ||
156 | set = global_ppriv[i].sets[0]; | ||
157 | retval = via_setFirst(set, &item); | ||
158 | while (retval) { | ||
159 | DRM_DEBUG("free video memory 0x%lx\n", item); | ||
160 | via_mmFreeMem((PMemBlock) item); | ||
161 | retval = via_setNext(set, &item); | ||
162 | } | ||
163 | via_setDestroy(set); | ||
164 | |||
165 | /* AGP Memory */ | ||
166 | set = global_ppriv[i].sets[1]; | ||
167 | retval = via_setFirst(set, &item); | ||
168 | while (retval) { | ||
169 | DRM_DEBUG("free agp memory 0x%lx\n", item); | ||
170 | via_mmFreeMem((PMemBlock) item); | ||
171 | retval = via_setNext(set, &item); | ||
172 | } | ||
173 | via_setDestroy(set); | ||
174 | global_ppriv[i].used = 0; | ||
175 | } | ||
176 | via_release_futex(dev_priv, context); | 96 | via_release_futex(dev_priv, context); |
177 | 97 | ||
178 | #if defined(__linux__) | ||
179 | /* Linux specific until context tracking code gets ported to BSD */ | 98 | /* Linux specific until context tracking code gets ported to BSD */ |
180 | /* Last context, perform cleanup */ | 99 | /* Last context, perform cleanup */ |
181 | if (dev->ctx_count == 1 && dev->dev_private) { | 100 | if (dev->ctx_count == 1 && dev->dev_private) { |
182 | DRM_DEBUG("Last Context\n"); | 101 | DRM_DEBUG("Last Context\n"); |
183 | if (dev->irq) | 102 | if (dev->irq) |
184 | drm_irq_uninstall(dev); | 103 | drm_irq_uninstall(dev); |
185 | |||
186 | via_cleanup_futex(dev_priv); | 104 | via_cleanup_futex(dev_priv); |
187 | via_do_cleanup_map(dev); | 105 | via_do_cleanup_map(dev); |
188 | } | 106 | } |
189 | #endif | ||
190 | |||
191 | return 1; | 107 | return 1; |
192 | } | 108 | } |
193 | 109 | ||
110 | void via_lastclose(struct drm_device *dev) | ||
111 | { | ||
112 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | ||
113 | |||
114 | if (!dev_priv) | ||
115 | return; | ||
116 | |||
117 | mutex_lock(&dev->struct_mutex); | ||
118 | drm_sman_cleanup(&dev_priv->sman); | ||
119 | dev_priv->vram_initialized = FALSE; | ||
120 | dev_priv->agp_initialized = FALSE; | ||
121 | mutex_unlock(&dev->struct_mutex); | ||
122 | } | ||
123 | |||
194 | int via_mem_alloc(DRM_IOCTL_ARGS) | 124 | int via_mem_alloc(DRM_IOCTL_ARGS) |
195 | { | 125 | { |
126 | DRM_DEVICE; | ||
127 | |||
196 | drm_via_mem_t mem; | 128 | drm_via_mem_t mem; |
129 | int retval = 0; | ||
130 | drm_memblock_item_t *item; | ||
131 | drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private; | ||
132 | unsigned long tmpSize; | ||
197 | 133 | ||
198 | DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, | 134 | DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, |
199 | sizeof(mem)); | 135 | sizeof(mem)); |
200 | 136 | ||
201 | switch (mem.type) { | 137 | if (mem.type > VIA_MEM_AGP) { |
202 | case VIA_MEM_VIDEO: | 138 | DRM_ERROR("Unknown memory type allocation\n"); |
203 | if (via_fb_alloc(&mem) < 0) | 139 | return DRM_ERR(EINVAL); |
204 | return -EFAULT; | ||
205 | DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, | ||
206 | sizeof(mem)); | ||
207 | return 0; | ||
208 | case VIA_MEM_AGP: | ||
209 | if (via_agp_alloc(&mem) < 0) | ||
210 | return -EFAULT; | ||
211 | DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, | ||
212 | sizeof(mem)); | ||
213 | return 0; | ||
214 | } | 140 | } |
215 | 141 | mutex_lock(&dev->struct_mutex); | |
216 | return -EFAULT; | 142 | if (FALSE == ((mem.type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized : |
217 | } | 143 | dev_priv->agp_initialized)) { |
218 | 144 | DRM_ERROR | |
219 | static int via_fb_alloc(drm_via_mem_t * mem) | 145 | ("Attempt to allocate from uninitialized memory manager.\n"); |
220 | { | 146 | mutex_unlock(&dev->struct_mutex); |
221 | drm_via_mm_t fb; | 147 | return DRM_ERR(EINVAL); |
222 | PMemBlock block; | ||
223 | int retval = 0; | ||
224 | |||
225 | if (!FBHeap) | ||
226 | return -1; | ||
227 | |||
228 | fb.size = mem->size; | ||
229 | fb.context = mem->context; | ||
230 | |||
231 | block = via_mmAllocMem(FBHeap, fb.size, 5, 0); | ||
232 | if (block) { | ||
233 | fb.offset = block->ofs; | ||
234 | fb.free = (unsigned long)block; | ||
235 | if (!add_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) { | ||
236 | DRM_DEBUG("adding to allocation set fails\n"); | ||
237 | via_mmFreeMem((PMemBlock) fb.free); | ||
238 | retval = -1; | ||
239 | } | ||
240 | } else { | ||
241 | fb.offset = 0; | ||
242 | fb.size = 0; | ||
243 | fb.free = 0; | ||
244 | retval = -1; | ||
245 | } | 148 | } |
246 | 149 | ||
247 | mem->offset = fb.offset; | 150 | tmpSize = (mem.size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT; |
248 | mem->index = fb.free; | 151 | item = drm_sman_alloc(&dev_priv->sman, mem.type, tmpSize, 0, |
249 | 152 | (unsigned long)priv); | |
250 | DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, | 153 | mutex_unlock(&dev->struct_mutex); |
251 | (int)fb.offset); | 154 | if (item) { |
252 | 155 | mem.offset = ((mem.type == VIA_MEM_VIDEO) ? | |
253 | return retval; | 156 | dev_priv->vram_offset : dev_priv->agp_offset) + |
254 | } | 157 | (item->mm-> |
255 | 158 | offset(item->mm, item->mm_info) << VIA_MM_ALIGN_SHIFT); | |
256 | static int via_agp_alloc(drm_via_mem_t * mem) | 159 | mem.index = item->user_hash.key; |
257 | { | ||
258 | drm_via_mm_t agp; | ||
259 | PMemBlock block; | ||
260 | int retval = 0; | ||
261 | |||
262 | if (!AgpHeap) | ||
263 | return -1; | ||
264 | |||
265 | agp.size = mem->size; | ||
266 | agp.context = mem->context; | ||
267 | |||
268 | block = via_mmAllocMem(AgpHeap, agp.size, 5, 0); | ||
269 | if (block) { | ||
270 | agp.offset = block->ofs; | ||
271 | agp.free = (unsigned long)block; | ||
272 | if (!add_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) { | ||
273 | DRM_DEBUG("adding to allocation set fails\n"); | ||
274 | via_mmFreeMem((PMemBlock) agp.free); | ||
275 | retval = -1; | ||
276 | } | ||
277 | } else { | 160 | } else { |
278 | agp.offset = 0; | 161 | mem.offset = 0; |
279 | agp.size = 0; | 162 | mem.size = 0; |
280 | agp.free = 0; | 163 | mem.index = 0; |
164 | DRM_DEBUG("Video memory allocation failed\n"); | ||
165 | retval = DRM_ERR(ENOMEM); | ||
281 | } | 166 | } |
167 | DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, sizeof(mem)); | ||
282 | 168 | ||
283 | mem->offset = agp.offset; | ||
284 | mem->index = agp.free; | ||
285 | |||
286 | DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size, | ||
287 | (unsigned int)agp.offset); | ||
288 | return retval; | 169 | return retval; |
289 | } | 170 | } |
290 | 171 | ||
291 | int via_mem_free(DRM_IOCTL_ARGS) | 172 | int via_mem_free(DRM_IOCTL_ARGS) |
292 | { | 173 | { |
174 | DRM_DEVICE; | ||
175 | drm_via_private_t *dev_priv = dev->dev_private; | ||
293 | drm_via_mem_t mem; | 176 | drm_via_mem_t mem; |
177 | int ret; | ||
294 | 178 | ||
295 | DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, | 179 | DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data, |
296 | sizeof(mem)); | 180 | sizeof(mem)); |
297 | 181 | ||
298 | switch (mem.type) { | 182 | mutex_lock(&dev->struct_mutex); |
183 | ret = drm_sman_free_key(&dev_priv->sman, mem.index); | ||
184 | mutex_unlock(&dev->struct_mutex); | ||
185 | DRM_DEBUG("free = 0x%lx\n", mem.index); | ||
299 | 186 | ||
300 | case VIA_MEM_VIDEO: | 187 | return ret; |
301 | if (via_fb_free(&mem) == 0) | ||
302 | return 0; | ||
303 | break; | ||
304 | case VIA_MEM_AGP: | ||
305 | if (via_agp_free(&mem) == 0) | ||
306 | return 0; | ||
307 | break; | ||
308 | } | ||
309 | |||
310 | return -EFAULT; | ||
311 | } | 188 | } |
312 | 189 | ||
313 | static int via_fb_free(drm_via_mem_t * mem) | ||
314 | { | ||
315 | drm_via_mm_t fb; | ||
316 | int retval = 0; | ||
317 | |||
318 | if (!FBHeap) { | ||
319 | return -1; | ||
320 | } | ||
321 | |||
322 | fb.free = mem->index; | ||
323 | fb.context = mem->context; | ||
324 | |||
325 | if (!fb.free) { | ||
326 | return -1; | ||
327 | |||
328 | } | ||
329 | |||
330 | via_mmFreeMem((PMemBlock) fb.free); | ||
331 | |||
332 | if (!del_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) { | ||
333 | retval = -1; | ||
334 | } | ||
335 | |||
336 | DRM_DEBUG("free fb, free = %ld\n", fb.free); | ||
337 | 190 | ||
338 | return retval; | 191 | void via_reclaim_buffers_locked(drm_device_t * dev, struct file *filp) |
339 | } | ||
340 | |||
341 | static int via_agp_free(drm_via_mem_t * mem) | ||
342 | { | 192 | { |
343 | drm_via_mm_t agp; | 193 | drm_via_private_t *dev_priv = dev->dev_private; |
344 | 194 | drm_file_t *priv = filp->private_data; | |
345 | int retval = 0; | ||
346 | 195 | ||
347 | agp.free = mem->index; | 196 | mutex_lock(&dev->struct_mutex); |
348 | agp.context = mem->context; | 197 | if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) { |
349 | 198 | mutex_unlock(&dev->struct_mutex); | |
350 | if (!agp.free) | 199 | return; |
351 | return -1; | ||
352 | |||
353 | via_mmFreeMem((PMemBlock) agp.free); | ||
354 | |||
355 | if (!del_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) { | ||
356 | retval = -1; | ||
357 | } | 200 | } |
358 | 201 | ||
359 | DRM_DEBUG("free agp, free = %ld\n", agp.free); | 202 | if (dev->driver->dma_quiescent) { |
203 | dev->driver->dma_quiescent(dev); | ||
204 | } | ||
360 | 205 | ||
361 | return retval; | 206 | drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv); |
207 | mutex_unlock(&dev->struct_mutex); | ||
208 | return; | ||
362 | } | 209 | } |