1 /* bufs.c -- IOCTLs to manage buffers -*- linux-c -*-
2 * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com
4 * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas.
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 * and/or sell copies of the Software, and to permit persons to whom the
12 * Software is furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 * DEALINGS IN THE SOFTWARE.
27 * Rickard E. (Rik) Faith <faith@precisioninsight.com>
31 #define __NO_VERSION__
32 #include <linux/config.h>
36 /* Compute order. Can be made faster. */
37 int drm_order(unsigned long size
)
42 for (order
= 0, tmp
= size
; tmp
>>= 1; ++order
);
43 if (size
& ~(1 << order
)) ++order
;
47 int drm_addmap(struct inode
*inode
, struct file
*filp
, unsigned int cmd
,
50 drm_file_t
*priv
= filp
->private_data
;
51 drm_device_t
*dev
= priv
->dev
;
54 if (!(filp
->f_mode
& 3)) return -EACCES
; /* Require read/write */
56 map
= drm_alloc(sizeof(*map
), DRM_MEM_MAPS
);
57 if (!map
) return -ENOMEM
;
58 if (copy_from_user(map
, (drm_map_t
*)arg
, sizeof(*map
))) {
59 drm_free(map
, sizeof(*map
), DRM_MEM_MAPS
);
63 DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n",
64 map
->offset
, map
->size
, map
->type
);
65 if ((map
->offset
& (~PAGE_MASK
)) || (map
->size
& (~PAGE_MASK
))) {
66 drm_free(map
, sizeof(*map
), DRM_MEM_MAPS
);
74 case _DRM_FRAME_BUFFER
:
76 if (map
->offset
+ map
->size
< map
->offset
77 || map
->offset
< virt_to_phys(high_memory
)) {
78 drm_free(map
, sizeof(*map
), DRM_MEM_MAPS
);
83 if (map
->type
== _DRM_FRAME_BUFFER
84 || (map
->flags
& _DRM_WRITE_COMBINING
)) {
85 map
->mtrr
= mtrr_add(map
->offset
, map
->size
,
89 map
->handle
= drm_ioremap(map
->offset
, map
->size
);
94 map
->handle
= (void *)drm_alloc_pages(drm_order(map
->size
)
97 DRM_DEBUG("%ld %d %p\n", map
->size
, drm_order(map
->size
),
100 drm_free(map
, sizeof(*map
), DRM_MEM_MAPS
);
103 map
->offset
= (unsigned long)map
->handle
;
104 if (map
->flags
& _DRM_CONTAINS_LOCK
) {
105 dev
->lock
.hw_lock
= map
->handle
; /* Pointer to lock */
109 drm_free(map
, sizeof(*map
), DRM_MEM_MAPS
);
113 down(&dev
->struct_sem
);
116 dev
->maplist
= drm_realloc(dev
->maplist
,
118 * sizeof(*dev
->maplist
),
120 * sizeof(*dev
->maplist
),
124 dev
->maplist
= drm_alloc(dev
->map_count
*sizeof(*dev
->maplist
),
127 dev
->maplist
[dev
->map_count
-1] = map
;
128 up(&dev
->struct_sem
);
130 copy_to_user_ret((drm_map_t
*)arg
, map
, sizeof(*map
), -EFAULT
);
131 if (map
->type
!= _DRM_SHM
) {
132 copy_to_user_ret(&((drm_map_t
*)arg
)->handle
,
140 int drm_addbufs(struct inode
*inode
, struct file
*filp
, unsigned int cmd
,
143 drm_file_t
*priv
= filp
->private_data
;
144 drm_device_t
*dev
= priv
->dev
;
145 drm_device_dma_t
*dma
= dev
->dma
;
146 drm_buf_desc_t request
;
152 drm_buf_entry_t
*entry
;
156 unsigned long offset
;
161 if (!dma
) return -EINVAL
;
163 copy_from_user_ret(&request
,
164 (drm_buf_desc_t
*)arg
,
168 count
= request
.count
;
169 order
= drm_order(request
.size
);
172 DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n",
173 request
.count
, request
.size
, size
, order
, dev
->queue_count
);
175 if (order
< DRM_MIN_ORDER
|| order
> DRM_MAX_ORDER
) return -EINVAL
;
176 if (dev
->queue_count
) return -EBUSY
; /* Not while in use */
178 alignment
= (request
.flags
& DRM_PAGE_ALIGN
) ? PAGE_ALIGN(size
) :size
;
179 page_order
= order
- PAGE_SHIFT
> 0 ? order
- PAGE_SHIFT
: 0;
180 total
= PAGE_SIZE
<< page_order
;
182 spin_lock(&dev
->count_lock
);
184 spin_unlock(&dev
->count_lock
);
187 atomic_inc(&dev
->buf_alloc
);
188 spin_unlock(&dev
->count_lock
);
190 down(&dev
->struct_sem
);
191 entry
= &dma
->bufs
[order
];
192 if (entry
->buf_count
) {
193 up(&dev
->struct_sem
);
194 atomic_dec(&dev
->buf_alloc
);
195 return -ENOMEM
; /* May only call once for each order */
198 entry
->buflist
= drm_alloc(count
* sizeof(*entry
->buflist
),
200 if (!entry
->buflist
) {
201 up(&dev
->struct_sem
);
202 atomic_dec(&dev
->buf_alloc
);
205 memset(entry
->buflist
, 0, count
* sizeof(*entry
->buflist
));
207 entry
->seglist
= drm_alloc(count
* sizeof(*entry
->seglist
),
209 if (!entry
->seglist
) {
210 drm_free(entry
->buflist
,
211 count
* sizeof(*entry
->buflist
),
213 up(&dev
->struct_sem
);
214 atomic_dec(&dev
->buf_alloc
);
217 memset(entry
->seglist
, 0, count
* sizeof(*entry
->seglist
));
219 dma
->pagelist
= drm_realloc(dma
->pagelist
,
220 dma
->page_count
* sizeof(*dma
->pagelist
),
221 (dma
->page_count
+ (count
<< page_order
))
222 * sizeof(*dma
->pagelist
),
224 DRM_DEBUG("pagelist: %d entries\n",
225 dma
->page_count
+ (count
<< page_order
));
228 entry
->buf_size
= size
;
229 entry
->page_order
= page_order
;
232 while (entry
->buf_count
< count
) {
233 if (!(page
= drm_alloc_pages(page_order
, DRM_MEM_DMA
))) break;
234 entry
->seglist
[entry
->seg_count
++] = page
;
235 for (i
= 0; i
< (1 << page_order
); i
++) {
236 DRM_DEBUG("page %d @ 0x%08lx\n",
237 dma
->page_count
+ page_count
,
238 page
+ PAGE_SIZE
* i
);
239 dma
->pagelist
[dma
->page_count
+ page_count
++]
240 = page
+ PAGE_SIZE
* i
;
243 offset
+ size
<= total
&& entry
->buf_count
< count
;
244 offset
+= alignment
, ++entry
->buf_count
) {
245 buf
= &entry
->buflist
[entry
->buf_count
];
246 buf
->idx
= dma
->buf_count
+ entry
->buf_count
;
247 buf
->total
= alignment
;
250 buf
->offset
= (dma
->byte_count
+ byte_count
+ offset
);
251 buf
->address
= (void *)(page
+ offset
);
255 init_waitqueue_head(&buf
->dma_wait
);
257 #if DRM_DMA_HISTOGRAM
258 buf
->time_queued
= 0;
259 buf
->time_dispatched
= 0;
260 buf
->time_completed
= 0;
263 DRM_DEBUG("buffer %d @ %p\n",
264 entry
->buf_count
, buf
->address
);
266 byte_count
+= PAGE_SIZE
<< page_order
;
269 dma
->buflist
= drm_realloc(dma
->buflist
,
270 dma
->buf_count
* sizeof(*dma
->buflist
),
271 (dma
->buf_count
+ entry
->buf_count
)
272 * sizeof(*dma
->buflist
),
274 for (i
= dma
->buf_count
; i
< dma
->buf_count
+ entry
->buf_count
; i
++)
275 dma
->buflist
[i
] = &entry
->buflist
[i
- dma
->buf_count
];
277 dma
->buf_count
+= entry
->buf_count
;
278 dma
->seg_count
+= entry
->seg_count
;
279 dma
->page_count
+= entry
->seg_count
<< page_order
;
280 dma
->byte_count
+= PAGE_SIZE
* (entry
->seg_count
<< page_order
);
282 drm_freelist_create(&entry
->freelist
, entry
->buf_count
);
283 for (i
= 0; i
< entry
->buf_count
; i
++) {
284 drm_freelist_put(dev
, &entry
->freelist
, &entry
->buflist
[i
]);
287 up(&dev
->struct_sem
);
289 request
.count
= entry
->buf_count
;
292 copy_to_user_ret((drm_buf_desc_t
*)arg
,
297 atomic_dec(&dev
->buf_alloc
);
301 int drm_infobufs(struct inode
*inode
, struct file
*filp
, unsigned int cmd
,
304 drm_file_t
*priv
= filp
->private_data
;
305 drm_device_t
*dev
= priv
->dev
;
306 drm_device_dma_t
*dma
= dev
->dma
;
307 drm_buf_info_t request
;
311 if (!dma
) return -EINVAL
;
313 spin_lock(&dev
->count_lock
);
314 if (atomic_read(&dev
->buf_alloc
)) {
315 spin_unlock(&dev
->count_lock
);
318 ++dev
->buf_use
; /* Can't allocate more after this call */
319 spin_unlock(&dev
->count_lock
);
321 copy_from_user_ret(&request
,
322 (drm_buf_info_t
*)arg
,
326 for (i
= 0, count
= 0; i
< DRM_MAX_ORDER
+1; i
++) {
327 if (dma
->bufs
[i
].buf_count
) ++count
;
330 DRM_DEBUG("count = %d\n", count
);
332 if (request
.count
>= count
) {
333 for (i
= 0, count
= 0; i
< DRM_MAX_ORDER
+1; i
++) {
334 if (dma
->bufs
[i
].buf_count
) {
335 copy_to_user_ret(&request
.list
[count
].count
,
336 &dma
->bufs
[i
].buf_count
,
340 copy_to_user_ret(&request
.list
[count
].size
,
341 &dma
->bufs
[i
].buf_size
,
342 sizeof(dma
->bufs
[0].buf_size
),
344 copy_to_user_ret(&request
.list
[count
].low_mark
,
350 copy_to_user_ret(&request
.list
[count
]
355 .freelist
.high_mark
),
357 DRM_DEBUG("%d %d %d %d %d\n",
359 dma
->bufs
[i
].buf_count
,
360 dma
->bufs
[i
].buf_size
,
361 dma
->bufs
[i
].freelist
.low_mark
,
362 dma
->bufs
[i
].freelist
.high_mark
);
367 request
.count
= count
;
369 copy_to_user_ret((drm_buf_info_t
*)arg
,
377 int drm_markbufs(struct inode
*inode
, struct file
*filp
, unsigned int cmd
,
380 drm_file_t
*priv
= filp
->private_data
;
381 drm_device_t
*dev
= priv
->dev
;
382 drm_device_dma_t
*dma
= dev
->dma
;
383 drm_buf_desc_t request
;
385 drm_buf_entry_t
*entry
;
387 if (!dma
) return -EINVAL
;
389 copy_from_user_ret(&request
,
390 (drm_buf_desc_t
*)arg
,
394 DRM_DEBUG("%d, %d, %d\n",
395 request
.size
, request
.low_mark
, request
.high_mark
);
396 order
= drm_order(request
.size
);
397 if (order
< DRM_MIN_ORDER
|| order
> DRM_MAX_ORDER
) return -EINVAL
;
398 entry
= &dma
->bufs
[order
];
400 if (request
.low_mark
< 0 || request
.low_mark
> entry
->buf_count
)
402 if (request
.high_mark
< 0 || request
.high_mark
> entry
->buf_count
)
405 entry
->freelist
.low_mark
= request
.low_mark
;
406 entry
->freelist
.high_mark
= request
.high_mark
;
411 int drm_freebufs(struct inode
*inode
, struct file
*filp
, unsigned int cmd
,
414 drm_file_t
*priv
= filp
->private_data
;
415 drm_device_t
*dev
= priv
->dev
;
416 drm_device_dma_t
*dma
= dev
->dma
;
417 drm_buf_free_t request
;
422 if (!dma
) return -EINVAL
;
424 copy_from_user_ret(&request
,
425 (drm_buf_free_t
*)arg
,
429 DRM_DEBUG("%d\n", request
.count
);
430 for (i
= 0; i
< request
.count
; i
++) {
431 copy_from_user_ret(&idx
,
435 if (idx
< 0 || idx
>= dma
->buf_count
) {
436 DRM_ERROR("Index %d (of %d max)\n",
437 idx
, dma
->buf_count
- 1);
440 buf
= dma
->buflist
[idx
];
441 if (buf
->pid
!= current
->pid
) {
442 DRM_ERROR("Process %d freeing buffer owned by %d\n",
443 current
->pid
, buf
->pid
);
446 drm_free_buffer(dev
, buf
);
452 int drm_mapbufs(struct inode
*inode
, struct file
*filp
, unsigned int cmd
,
455 drm_file_t
*priv
= filp
->private_data
;
456 drm_device_t
*dev
= priv
->dev
;
457 drm_device_dma_t
*dma
= dev
->dma
;
460 unsigned long virtual;
461 unsigned long address
;
462 drm_buf_map_t request
;
465 if (!dma
) return -EINVAL
;
469 spin_lock(&dev
->count_lock
);
470 if (atomic_read(&dev
->buf_alloc
)) {
471 spin_unlock(&dev
->count_lock
);
474 ++dev
->buf_use
; /* Can't allocate more after this call */
475 spin_unlock(&dev
->count_lock
);
477 copy_from_user_ret(&request
,
478 (drm_buf_map_t
*)arg
,
482 if (request
.count
>= dma
->buf_count
) {
483 down(¤t
->mm
->mmap_sem
);
484 virtual = do_mmap(filp
, 0, dma
->byte_count
,
485 PROT_READ
|PROT_WRITE
, MAP_SHARED
, 0);
486 up(¤t
->mm
->mmap_sem
);
487 if (virtual > -1024UL) {
489 retcode
= (signed long)virtual;
492 request
.virtual = (void *)virtual;
494 for (i
= 0; i
< dma
->buf_count
; i
++) {
495 if (copy_to_user(&request
.list
[i
].idx
,
496 &dma
->buflist
[i
]->idx
,
497 sizeof(request
.list
[0].idx
))) {
501 if (copy_to_user(&request
.list
[i
].total
,
502 &dma
->buflist
[i
]->total
,
503 sizeof(request
.list
[0].total
))) {
507 if (copy_to_user(&request
.list
[i
].used
,
513 address
= virtual + dma
->buflist
[i
]->offset
;
514 if (copy_to_user(&request
.list
[i
].address
,
523 request
.count
= dma
->buf_count
;
524 DRM_DEBUG("%d buffers, retcode = %d\n", request
.count
, retcode
);
526 copy_to_user_ret((drm_buf_map_t
*)arg
,