aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/drm/drm_bufs.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/drm/drm_bufs.c')
-rw-r--r--drivers/char/drm/drm_bufs.c229
1 files changed, 159 insertions, 70 deletions
diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c
index 06b01215fdf3..fcc8d244f46f 100644
--- a/drivers/char/drm/drm_bufs.c
+++ b/drivers/char/drm/drm_bufs.c
@@ -36,29 +36,33 @@
36#include <linux/vmalloc.h> 36#include <linux/vmalloc.h>
37#include "drmP.h" 37#include "drmP.h"
38 38
39/** 39unsigned long drm_get_resource_start(drm_device_t *dev, unsigned int resource)
40 * Compute size order. Returns the exponent of the smaller power of two which
41 * is greater or equal to given number.
42 *
43 * \param size size.
44 * \return order.
45 *
46 * \todo Can be made faster.
47 */
48int drm_order( unsigned long size )
49{ 40{
50 int order; 41 return pci_resource_start(dev->pdev, resource);
51 unsigned long tmp; 42}
43EXPORT_SYMBOL(drm_get_resource_start);
52 44
53 for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) 45unsigned long drm_get_resource_len(drm_device_t *dev, unsigned int resource)
54 ; 46{
47 return pci_resource_len(dev->pdev, resource);
48}
49EXPORT_SYMBOL(drm_get_resource_len);
55 50
56 if (size & (size - 1)) 51static drm_local_map_t *drm_find_matching_map(drm_device_t *dev,
57 ++order; 52 drm_local_map_t *map)
53{
54 struct list_head *list;
58 55
59 return order; 56 list_for_each(list, &dev->maplist->head) {
57 drm_map_list_t *entry = list_entry(list, drm_map_list_t, head);
58 if (entry->map && map->type == entry->map->type &&
59 entry->map->offset == map->offset) {
60 return entry->map;
61 }
62 }
63
64 return NULL;
60} 65}
61EXPORT_SYMBOL(drm_order);
62 66
63#ifdef CONFIG_COMPAT 67#ifdef CONFIG_COMPAT
64/* 68/*
@@ -89,6 +93,7 @@ int drm_addmap(drm_device_t * dev, unsigned int offset,
89 drm_map_t *map; 93 drm_map_t *map;
90 drm_map_list_t *list; 94 drm_map_list_t *list;
91 drm_dma_handle_t *dmah; 95 drm_dma_handle_t *dmah;
96 drm_local_map_t *found_map;
92 97
93 map = drm_alloc( sizeof(*map), DRM_MEM_MAPS ); 98 map = drm_alloc( sizeof(*map), DRM_MEM_MAPS );
94 if ( !map ) 99 if ( !map )
@@ -129,6 +134,24 @@ int drm_addmap(drm_device_t * dev, unsigned int offset,
129#ifdef __alpha__ 134#ifdef __alpha__
130 map->offset += dev->hose->mem_space->start; 135 map->offset += dev->hose->mem_space->start;
131#endif 136#endif
137 /* Some drivers preinitialize some maps, without the X Server
138 * needing to be aware of it. Therefore, we just return success
139 * when the server tries to create a duplicate map.
140 */
141 found_map = drm_find_matching_map(dev, map);
142 if (found_map != NULL) {
143 if (found_map->size != map->size) {
144 DRM_DEBUG("Matching maps of type %d with "
145 "mismatched sizes, (%ld vs %ld)\n",
146 map->type, map->size, found_map->size);
147 found_map->size = map->size;
148 }
149
150 drm_free(map, sizeof(*map), DRM_MEM_MAPS);
151 *map_ptr = found_map;
152 return 0;
153 }
154
132 if (drm_core_has_MTRR(dev)) { 155 if (drm_core_has_MTRR(dev)) {
133 if ( map->type == _DRM_FRAME_BUFFER || 156 if ( map->type == _DRM_FRAME_BUFFER ||
134 (map->flags & _DRM_WRITE_COMBINING) ) { 157 (map->flags & _DRM_WRITE_COMBINING) ) {
@@ -270,93 +293,136 @@ int drm_addmap_ioctl(struct inode *inode, struct file *filp,
270 * 293 *
271 * \sa drm_addmap 294 * \sa drm_addmap
272 */ 295 */
273int drm_rmmap(drm_device_t *dev, void *handle) 296int drm_rmmap_locked(drm_device_t *dev, drm_local_map_t *map)
274{ 297{
275 struct list_head *list; 298 struct list_head *list;
276 drm_map_list_t *r_list = NULL; 299 drm_map_list_t *r_list = NULL;
277 drm_vma_entry_t *pt, *prev; 300 drm_dma_handle_t dmah;
278 drm_map_t *map;
279 int found_maps = 0;
280 301
281 down(&dev->struct_sem); 302 /* Find the list entry for the map and remove it */
282 list = &dev->maplist->head;
283 list_for_each(list, &dev->maplist->head) { 303 list_for_each(list, &dev->maplist->head) {
284 r_list = list_entry(list, drm_map_list_t, head); 304 r_list = list_entry(list, drm_map_list_t, head);
285 305
286 if(r_list->map && 306 if (r_list->map == map) {
287 r_list->map->handle == handle && 307 list_del(list);
288 r_list->map->flags & _DRM_REMOVABLE) break; 308 drm_free(list, sizeof(*list), DRM_MEM_MAPS);
309 break;
310 }
289 } 311 }
290 312
291 /* List has wrapped around to the head pointer, or its empty we didn't 313 /* List has wrapped around to the head pointer, or it's empty and we
292 * find anything. 314 * didn't find anything.
293 */ 315 */
294 if(list == (&dev->maplist->head)) { 316 if (list == (&dev->maplist->head)) {
295 up(&dev->struct_sem);
296 return -EINVAL; 317 return -EINVAL;
297 } 318 }
298 map = r_list->map;
299 list_del(list);
300 drm_free(list, sizeof(*list), DRM_MEM_MAPS);
301
302 for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) {
303 if (pt->vma->vm_private_data == map) found_maps++;
304 }
305 319
306 if(!found_maps) { 320 switch (map->type) {
307 drm_dma_handle_t dmah; 321 case _DRM_REGISTERS:
308 322 drm_ioremapfree(map->handle, map->size, dev);
309 switch (map->type) { 323 /* FALLTHROUGH */
310 case _DRM_REGISTERS: 324 case _DRM_FRAME_BUFFER:
311 case _DRM_FRAME_BUFFER: 325 if (drm_core_has_MTRR(dev) && map->mtrr >= 0) {
312 if (drm_core_has_MTRR(dev)) { 326 int retcode;
313 if (map->mtrr >= 0) { 327 retcode = mtrr_del(map->mtrr, map->offset,
314 int retcode; 328 map->size);
315 retcode = mtrr_del(map->mtrr, 329 DRM_DEBUG ("mtrr_del=%d\n", retcode);
316 map->offset,
317 map->size);
318 DRM_DEBUG("mtrr_del = %d\n", retcode);
319 }
320 }
321 drm_ioremapfree(map->handle, map->size, dev);
322 break;
323 case _DRM_SHM:
324 vfree(map->handle);
325 break;
326 case _DRM_AGP:
327 case _DRM_SCATTER_GATHER:
328 break;
329 case _DRM_CONSISTENT:
330 dmah.vaddr = map->handle;
331 dmah.busaddr = map->offset;
332 dmah.size = map->size;
333 __drm_pci_free(dev, &dmah);
334 break;
335 } 330 }
336 drm_free(map, sizeof(*map), DRM_MEM_MAPS); 331 break;
332 case _DRM_SHM:
333 vfree(map->handle);
334 break;
335 case _DRM_AGP:
336 case _DRM_SCATTER_GATHER:
337 break;
338 case _DRM_CONSISTENT:
339 dmah.vaddr = map->handle;
340 dmah.busaddr = map->offset;
341 dmah.size = map->size;
342 __drm_pci_free(dev, &dmah);
343 break;
337 } 344 }
338 up(&dev->struct_sem); 345 drm_free(map, sizeof(*map), DRM_MEM_MAPS);
346
339 return 0; 347 return 0;
340} 348}
349EXPORT_SYMBOL(drm_rmmap_locked);
350
351int drm_rmmap(drm_device_t *dev, drm_local_map_t *map)
352{
353 int ret;
354
355 down(&dev->struct_sem);
356 ret = drm_rmmap_locked(dev, map);
357 up(&dev->struct_sem);
358
359 return ret;
360}
341EXPORT_SYMBOL(drm_rmmap); 361EXPORT_SYMBOL(drm_rmmap);
342 362
363/* The rmmap ioctl appears to be unnecessary. All mappings are torn down on
364 * the last close of the device, and this is necessary for cleanup when things
365 * exit uncleanly. Therefore, having userland manually remove mappings seems
366 * like a pointless exercise since they're going away anyway.
367 *
368 * One use case might be after addmap is allowed for normal users for SHM and
369 * gets used by drivers that the server doesn't need to care about. This seems
370 * unlikely.
371 */
343int drm_rmmap_ioctl(struct inode *inode, struct file *filp, 372int drm_rmmap_ioctl(struct inode *inode, struct file *filp,
344 unsigned int cmd, unsigned long arg) 373 unsigned int cmd, unsigned long arg)
345{ 374{
346 drm_file_t *priv = filp->private_data; 375 drm_file_t *priv = filp->private_data;
347 drm_device_t *dev = priv->head->dev; 376 drm_device_t *dev = priv->head->dev;
348 drm_map_t request; 377 drm_map_t request;
378 drm_local_map_t *map = NULL;
379 struct list_head *list;
380 int ret;
349 381
350 if (copy_from_user(&request, (drm_map_t __user *)arg, sizeof(request))) { 382 if (copy_from_user(&request, (drm_map_t __user *)arg, sizeof(request))) {
351 return -EFAULT; 383 return -EFAULT;
352 } 384 }
353 385
354 return drm_rmmap(dev, request.handle); 386 down(&dev->struct_sem);
387 list_for_each(list, &dev->maplist->head) {
388 drm_map_list_t *r_list = list_entry(list, drm_map_list_t, head);
389
390 if (r_list->map &&
391 r_list->map->handle == request.handle &&
392 r_list->map->flags & _DRM_REMOVABLE) {
393 map = r_list->map;
394 break;
395 }
396 }
397
398 /* List has wrapped around to the head pointer, or its empty we didn't
399 * find anything.
400 */
401 if (list == (&dev->maplist->head)) {
402 up(&dev->struct_sem);
403 return -EINVAL;
404 }
405
406 if (!map)
407 return -EINVAL;
408
409 /* Register and framebuffer maps are permanent */
410 if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
411 up(&dev->struct_sem);
412 return 0;
413 }
414
415 ret = drm_rmmap_locked(dev, map);
416
417 up(&dev->struct_sem);
418
419 return ret;
355} 420}
356 421
357/** 422/**
358 * Cleanup after an error on one of the addbufs() functions. 423 * Cleanup after an error on one of the addbufs() functions.
359 * 424 *
425 * \param dev DRM device.
360 * \param entry buffer entry where the error occurred. 426 * \param entry buffer entry where the error occurred.
361 * 427 *
362 * Frees any pages and buffers associated with the given entry. 428 * Frees any pages and buffers associated with the given entry.
@@ -1470,3 +1536,26 @@ int drm_mapbufs( struct inode *inode, struct file *filp,
1470 return retcode; 1536 return retcode;
1471} 1537}
1472 1538
1539/**
1540 * Compute size order. Returns the exponent of the smaller power of two which
1541 * is greater or equal to given number.
1542 *
1543 * \param size size.
1544 * \return order.
1545 *
1546 * \todo Can be made faster.
1547 */
1548int drm_order( unsigned long size )
1549{
1550 int order;
1551 unsigned long tmp;
1552
1553 for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++)
1554 ;
1555
1556 if (size & (size - 1))
1557 ++order;
1558
1559 return order;
1560}
1561EXPORT_SYMBOL(drm_order);