diff options
Diffstat (limited to 'drivers/char/drm/drm_bufs.c')
-rw-r--r-- | drivers/char/drm/drm_bufs.c | 229 |
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 | /** | 39 | unsigned 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 | */ | ||
48 | int drm_order( unsigned long size ) | ||
49 | { | 40 | { |
50 | int order; | 41 | return pci_resource_start(dev->pdev, resource); |
51 | unsigned long tmp; | 42 | } |
43 | EXPORT_SYMBOL(drm_get_resource_start); | ||
52 | 44 | ||
53 | for (order = 0, tmp = size >> 1; tmp; tmp >>= 1, order++) | 45 | unsigned long drm_get_resource_len(drm_device_t *dev, unsigned int resource) |
54 | ; | 46 | { |
47 | return pci_resource_len(dev->pdev, resource); | ||
48 | } | ||
49 | EXPORT_SYMBOL(drm_get_resource_len); | ||
55 | 50 | ||
56 | if (size & (size - 1)) | 51 | static 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 | } |
61 | EXPORT_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 | */ |
273 | int drm_rmmap(drm_device_t *dev, void *handle) | 296 | int 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 | } |
349 | EXPORT_SYMBOL(drm_rmmap_locked); | ||
350 | |||
351 | int 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 | } | ||
341 | EXPORT_SYMBOL(drm_rmmap); | 361 | EXPORT_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 | */ | ||
343 | int drm_rmmap_ioctl(struct inode *inode, struct file *filp, | 372 | int 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 | */ | ||
1548 | int 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 | } | ||
1561 | EXPORT_SYMBOL(drm_order); | ||