2 * Copyright © 2008 Nicolai Haehnle
3 * Copyright © 2008 Dave Airlie
4 * Copyright © 2008 Jérôme Glisse
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
23 * The above copyright notice and this permission notice (including the
24 * next paragraph) shall be included in all copies or substantial portions
29 * Aapo Tahkola <aet@rasterburn.org>
30 * Nicolai Haehnle <prefect_@gmx.net>
32 * Jérôme Glisse <glisse@freedesktop.org>
41 #include <sys/ioctl.h>
44 #include "radeon_drm.h"
45 #include "radeon_bo.h"
46 #include "radeon_bo_legacy.h"
47 #include "radeon_ioctl.h"
51 struct radeon_bo base
;
52 driTextureObject tobj_base
;
58 int got_dri_texture_obj
;
61 driTextureObject dri_texture_obj
;
63 struct bo_legacy
*next
, *prev
;
64 struct bo_legacy
*pnext
, *pprev
;
67 struct bo_manager_legacy
{
68 struct radeon_bo_manager base
;
70 unsigned nfree_handles
;
71 unsigned cfree_handles
;
74 struct bo_legacy pending_bos
;
76 uint32_t texture_offset
;
77 unsigned dma_alloc_size
;
79 driTextureObject texture_swapped
;
80 driTexHeap
*texture_heap
;
81 struct radeon_screen
*screen
;
82 unsigned *free_handles
;
85 static void bo_legacy_tobj_destroy(void *data
, driTextureObject
*t
)
87 struct bo_legacy
*bo_legacy
;
89 bo_legacy
= (struct bo_legacy
*)((char*)t
)-sizeof(struct radeon_bo
);
90 bo_legacy
->got_dri_texture_obj
= 0;
91 bo_legacy
->validated
= 0;
94 static int legacy_new_handle(struct bo_manager_legacy
*bom
, uint32_t *handle
)
99 if (bom
->nhandle
== 0xFFFFFFFF) {
102 if (bom
->cfree_handles
> 0) {
103 tmp
= bom
->free_handles
[--bom
->cfree_handles
];
104 while (!bom
->free_handles
[bom
->cfree_handles
- 1]) {
105 bom
->cfree_handles
--;
106 if (bom
->cfree_handles
<= 0) {
107 bom
->cfree_handles
= 0;
111 bom
->cfree_handles
= 0;
112 tmp
= bom
->nhandle
++;
119 static int legacy_free_handle(struct bo_manager_legacy
*bom
, uint32_t handle
)
126 if (handle
== (bom
->nhandle
- 1)) {
130 for (i
= bom
->cfree_handles
- 1; i
>= 0; i
--) {
131 if (bom
->free_handles
[i
] == (bom
->nhandle
- 1)) {
133 bom
->free_handles
[i
] = 0;
136 while (!bom
->free_handles
[bom
->cfree_handles
- 1]) {
137 bom
->cfree_handles
--;
138 if (bom
->cfree_handles
<= 0) {
139 bom
->cfree_handles
= 0;
144 if (bom
->cfree_handles
< bom
->nfree_handles
) {
145 bom
->free_handles
[bom
->cfree_handles
++] = handle
;
148 bom
->nfree_handles
+= 0x100;
149 handles
= (uint32_t*)realloc(bom
->free_handles
, bom
->nfree_handles
* 4);
150 if (handles
== NULL
) {
151 bom
->nfree_handles
-= 0x100;
154 bom
->free_handles
= handles
;
155 bom
->free_handles
[bom
->cfree_handles
++] = handle
;
159 static void legacy_get_current_age(struct bo_manager_legacy
*boml
)
161 drm_radeon_getparam_t gp
;
164 gp
.param
= RADEON_PARAM_LAST_CLEAR
;
165 gp
.value
= (int *)&boml
->current_age
;
166 r
= drmCommandWriteRead(boml
->base
.fd
, DRM_RADEON_GETPARAM
,
169 fprintf(stderr
, "%s: drmRadeonGetParam: %d\n", __FUNCTION__
, r
);
174 static int legacy_is_pending(struct radeon_bo
*bo
)
176 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
177 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
179 if (bo_legacy
->is_pending
<= 0) {
180 bo_legacy
->is_pending
= 0;
183 if (boml
->current_age
>= bo_legacy
->pending
) {
184 if (boml
->pending_bos
.pprev
== bo_legacy
) {
185 boml
->pending_bos
.pprev
= bo_legacy
->pprev
;
187 bo_legacy
->pprev
->pnext
= bo_legacy
->pnext
;
188 if (bo_legacy
->pnext
) {
189 bo_legacy
->pnext
->pprev
= bo_legacy
->pprev
;
191 while (bo_legacy
->is_pending
--) {
194 bo_legacy
->is_pending
= 0;
201 static int legacy_wait_pending(struct radeon_bo
*bo
)
203 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
204 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
206 if (!bo_legacy
->is_pending
) {
209 /* FIXME: lockup and userspace busy looping that's all the folks */
210 legacy_get_current_age(boml
);
211 while (legacy_is_pending(bo
)) {
213 legacy_get_current_age(boml
);
218 static void legacy_track_pending(struct bo_manager_legacy
*boml
)
220 struct bo_legacy
*bo_legacy
;
221 struct bo_legacy
*next
;
223 legacy_get_current_age(boml
);
224 bo_legacy
= boml
->pending_bos
.pnext
;
226 next
= bo_legacy
->pnext
;
227 if (legacy_is_pending(&(bo_legacy
->base
))) {
233 static struct bo_legacy
*bo_allocate(struct bo_manager_legacy
*boml
,
239 struct bo_legacy
*bo_legacy
;
241 bo_legacy
= (struct bo_legacy
*)calloc(1, sizeof(struct bo_legacy
));
242 if (bo_legacy
== NULL
) {
245 bo_legacy
->base
.bom
= (struct radeon_bo_manager
*)boml
;
246 bo_legacy
->base
.handle
= 0;
247 bo_legacy
->base
.size
= size
;
248 bo_legacy
->base
.alignment
= alignment
;
249 bo_legacy
->base
.domains
= domains
;
250 bo_legacy
->base
.flags
= flags
;
251 bo_legacy
->base
.ptr
= NULL
;
252 bo_legacy
->map_count
= 0;
253 bo_legacy
->next
= NULL
;
254 bo_legacy
->prev
= NULL
;
255 bo_legacy
->got_dri_texture_obj
= 0;
256 bo_legacy
->pnext
= NULL
;
257 bo_legacy
->pprev
= NULL
;
258 bo_legacy
->next
= boml
->bos
.next
;
259 bo_legacy
->prev
= &boml
->bos
;
260 boml
->bos
.next
= bo_legacy
;
261 if (bo_legacy
->next
) {
262 bo_legacy
->next
->prev
= bo_legacy
;
267 static int bo_dma_alloc(struct radeon_bo
*bo
)
269 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
270 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
271 drm_radeon_mem_alloc_t alloc
;
276 /* align size on 4Kb */
277 size
= (((4 * 1024) - 1) + bo
->size
) & ~((4 * 1024) - 1);
278 alloc
.region
= RADEON_MEM_REGION_GART
;
279 alloc
.alignment
= bo_legacy
->base
.alignment
;
281 alloc
.region_offset
= &base_offset
;
282 r
= drmCommandWriteRead(bo
->bom
->fd
,
287 /* ptr is set to NULL if dma allocation failed */
288 bo_legacy
->ptr
= NULL
;
292 bo_legacy
->ptr
= boml
->screen
->gartTextures
.map
+ base_offset
;
293 bo_legacy
->offset
= boml
->screen
->gart_texture_offset
+ base_offset
;
295 boml
->dma_alloc_size
+= size
;
299 static int bo_dma_free(struct radeon_bo
*bo
)
301 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
302 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
303 drm_radeon_mem_free_t memfree
;
306 if (bo_legacy
->ptr
== NULL
) {
307 /* ptr is set to NULL if dma allocation failed */
310 legacy_get_current_age(boml
);
311 memfree
.region
= RADEON_MEM_REGION_GART
;
312 memfree
.region_offset
= bo_legacy
->offset
;
313 memfree
.region_offset
-= boml
->screen
->gart_texture_offset
;
314 r
= drmCommandWrite(boml
->base
.fd
,
319 fprintf(stderr
, "Failed to free bo[%p] at %08x\n",
320 &bo_legacy
->base
, memfree
.region_offset
);
321 fprintf(stderr
, "ret = %s\n", strerror(-r
));
324 boml
->dma_alloc_size
-= bo_legacy
->base
.size
;
328 static void bo_free(struct bo_legacy
*bo_legacy
)
330 struct bo_manager_legacy
*boml
;
332 if (bo_legacy
== NULL
) {
335 boml
= (struct bo_manager_legacy
*)bo_legacy
->base
.bom
;
336 bo_legacy
->prev
->next
= bo_legacy
->next
;
337 if (bo_legacy
->next
) {
338 bo_legacy
->next
->prev
= bo_legacy
->prev
;
340 if (!bo_legacy
->static_bo
) {
341 legacy_free_handle(boml
, bo_legacy
->base
.handle
);
342 if (bo_legacy
->base
.domains
& RADEON_GEM_DOMAIN_GTT
) {
344 bo_dma_free(&bo_legacy
->base
);
346 /* free backing store */
347 free(bo_legacy
->ptr
);
350 memset(bo_legacy
, 0 , sizeof(struct bo_legacy
));
354 static struct radeon_bo
*bo_open(struct radeon_bo_manager
*bom
,
361 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bom
;
362 struct bo_legacy
*bo_legacy
;
366 bo_legacy
= boml
->bos
.next
;
368 if (bo_legacy
->base
.handle
== handle
) {
369 radeon_bo_ref(&(bo_legacy
->base
));
370 return (struct radeon_bo
*)bo_legacy
;
372 bo_legacy
= bo_legacy
->next
;
377 bo_legacy
= bo_allocate(boml
, size
, alignment
, domains
, flags
);
378 bo_legacy
->static_bo
= 0;
379 r
= legacy_new_handle(boml
, &bo_legacy
->base
.handle
);
384 if (bo_legacy
->base
.domains
& RADEON_GEM_DOMAIN_GTT
) {
385 legacy_track_pending(boml
);
387 r
= bo_dma_alloc(&(bo_legacy
->base
));
389 fprintf(stderr
, "Ran out of GART memory (for %d)!\n", size
);
390 fprintf(stderr
, "Please consider adjusting GARTSize option.\n");
396 bo_legacy
->ptr
= malloc(bo_legacy
->base
.size
);
397 if (bo_legacy
->ptr
== NULL
) {
402 radeon_bo_ref(&(bo_legacy
->base
));
403 return (struct radeon_bo
*)bo_legacy
;
406 static void bo_ref(struct radeon_bo
*bo
)
410 static void bo_unref(struct radeon_bo
*bo
)
412 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
415 bo_legacy
->prev
->next
= bo_legacy
->next
;
416 if (bo_legacy
->next
) {
417 bo_legacy
->next
->prev
= bo_legacy
->prev
;
419 if (!bo_legacy
->is_pending
) {
425 static int bo_map(struct radeon_bo
*bo
, int write
)
427 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
428 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
430 legacy_wait_pending(bo
);
431 bo_legacy
->validated
= 0;
432 bo_legacy
->dirty
= 1;
433 bo_legacy
->map_count
++;
434 bo
->ptr
= bo_legacy
->ptr
;
435 /* Read the first pixel in the frame buffer. This should
436 * be a noop, right? In fact without this conform fails as reading
437 * from the framebuffer sometimes produces old results -- the
438 * on-card read cache gets mixed up and doesn't notice that the
439 * framebuffer has been updated.
441 * Note that we should probably be reading some otherwise unused
442 * region of VRAM, otherwise we might get incorrect results when
443 * reading pixels from the top left of the screen.
445 * I found this problem on an R420 with glean's texCube test.
446 * Note that the R200 span code also *writes* the first pixel in the
447 * framebuffer, but I've found this to be unnecessary.
448 * -- Nicolai Hähnle, June 2008
452 volatile int *buf
= (int*)boml
->screen
->driScreen
->pFB
;
459 static int bo_unmap(struct radeon_bo
*bo
)
461 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
463 if (--bo_legacy
->map_count
> 0) {
470 static struct radeon_bo_funcs bo_legacy_funcs
= {
478 static int bo_vram_validate(struct radeon_bo
*bo
,
482 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
483 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
486 if (!bo_legacy
->got_dri_texture_obj
) {
487 make_empty_list(&bo_legacy
->dri_texture_obj
);
488 bo_legacy
->dri_texture_obj
.totalSize
= bo
->size
;
489 r
= driAllocateTexture(&boml
->texture_heap
, 1,
490 &bo_legacy
->dri_texture_obj
);
492 uint8_t *segfault
=NULL
;
493 fprintf(stderr
, "Ouch! vram_validate failed %d\n", r
);
497 bo_legacy
->offset
= boml
->texture_offset
+
498 bo_legacy
->dri_texture_obj
.memBlock
->ofs
;
499 bo_legacy
->got_dri_texture_obj
= 1;
500 bo_legacy
->dirty
= 1;
502 if (bo_legacy
->dirty
) {
503 /* Copy to VRAM using a blit.
504 * All memory is 4K aligned. We're using 1024 pixels wide blits.
506 drm_radeon_texture_t tex
;
507 drm_radeon_tex_image_t tmp
;
510 tex
.offset
= bo_legacy
->offset
;
512 assert(!(tex
.offset
& 1023));
516 if (bo
->size
< 4096) {
517 tmp
.width
= (bo
->size
+ 3) / 4;
521 tmp
.height
= (bo
->size
+ 4095) / 4096;
523 tmp
.data
= bo_legacy
->ptr
;
524 tex
.format
= RADEON_TXFORMAT_ARGB8888
;
525 tex
.width
= tmp
.width
;
526 tex
.height
= tmp
.height
;
527 tex
.pitch
= MAX2(tmp
.width
/ 16, 1);
529 ret
= drmCommandWriteRead(bo
->bom
->fd
,
532 sizeof(drm_radeon_texture_t
));
534 if (RADEON_DEBUG
& DEBUG_IOCTL
)
535 fprintf(stderr
, "DRM_RADEON_TEXTURE: again!\n");
538 } while (ret
== -EAGAIN
);
539 bo_legacy
->dirty
= 0;
544 int radeon_bo_legacy_validate(struct radeon_bo
*bo
,
548 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
551 if (bo_legacy
->map_count
) {
552 fprintf(stderr
, "bo(%p, %d) is mapped (%d) can't valide it.\n",
553 bo
, bo
->size
, bo_legacy
->map_count
);
556 if (bo_legacy
->static_bo
|| bo_legacy
->validated
) {
557 *soffset
= bo_legacy
->offset
;
558 *eoffset
= bo_legacy
->offset
+ bo
->size
;
561 if (!(bo
->domains
& RADEON_GEM_DOMAIN_GTT
)) {
562 r
= bo_vram_validate(bo
, soffset
, eoffset
);
567 *soffset
= bo_legacy
->offset
;
568 *eoffset
= bo_legacy
->offset
+ bo
->size
;
569 bo_legacy
->validated
= 1;
573 void radeon_bo_legacy_pending(struct radeon_bo
*bo
, uint32_t pending
)
575 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bo
->bom
;
576 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
578 bo_legacy
->pending
= pending
;
579 bo_legacy
->is_pending
+= 1;
580 /* add to pending list */
582 if (bo_legacy
->is_pending
> 1) {
585 bo_legacy
->pprev
= boml
->pending_bos
.pprev
;
586 bo_legacy
->pnext
= NULL
;
587 bo_legacy
->pprev
->pnext
= bo_legacy
;
588 boml
->pending_bos
.pprev
= bo_legacy
;
592 void radeon_bo_manager_legacy_shutdown(struct radeon_bo_manager
*bom
)
594 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bom
;
595 struct bo_legacy
*bo_legacy
;
600 bo_legacy
= boml
->bos
.next
;
602 struct bo_legacy
*next
;
604 next
= bo_legacy
->next
;
608 free(boml
->free_handles
);
612 struct radeon_bo_manager
*radeon_bo_manager_legacy(struct radeon_screen
*scrn
)
614 struct bo_manager_legacy
*bom
;
615 struct bo_legacy
*bo
;
618 bom
= (struct bo_manager_legacy
*)
619 calloc(1, sizeof(struct bo_manager_legacy
));
624 bom
->texture_heap
= driCreateTextureHeap(0,
628 RADEON_NR_TEX_REGIONS
,
629 (drmTextureRegionPtr
)scrn
->sarea
->tex_list
[0],
630 &scrn
->sarea
->tex_age
[0],
631 &bom
->texture_swapped
,
632 sizeof(struct bo_legacy
),
633 &bo_legacy_tobj_destroy
);
634 bom
->texture_offset
= scrn
->texOffset
[0];
636 bom
->base
.funcs
= &bo_legacy_funcs
;
637 bom
->base
.fd
= scrn
->driScreen
->fd
;
638 bom
->bos
.next
= NULL
;
639 bom
->bos
.prev
= NULL
;
640 bom
->pending_bos
.pprev
= &bom
->pending_bos
;
641 bom
->pending_bos
.pnext
= NULL
;
643 bom
->fb_location
= scrn
->fbLocation
;
645 bom
->cfree_handles
= 0;
646 bom
->nfree_handles
= 0x400;
647 bom
->free_handles
= (uint32_t*)malloc(bom
->nfree_handles
* 4);
648 if (bom
->free_handles
== NULL
) {
649 radeon_bo_manager_legacy_shutdown((struct radeon_bo_manager
*)bom
);
653 /* biggest framebuffer size */
656 bo
= bo_allocate(bom
, size
, 0, RADEON_GEM_DOMAIN_VRAM
, 0);
658 radeon_bo_manager_legacy_shutdown((struct radeon_bo_manager
*)bom
);
661 if (scrn
->sarea
->tiling_enabled
) {
662 bo
->base
.flags
= RADEON_BO_FLAGS_MACRO_TILE
;
665 bo
->offset
= bom
->screen
->frontOffset
+ bom
->fb_location
;
666 bo
->base
.handle
= bo
->offset
;
667 bo
->ptr
= scrn
->driScreen
->pFB
+ bom
->screen
->frontOffset
;
668 if (bo
->base
.handle
> bom
->nhandle
) {
669 bom
->nhandle
= bo
->base
.handle
+ 1;
672 bo
= bo_allocate(bom
, size
, 0, RADEON_GEM_DOMAIN_VRAM
, 0);
674 radeon_bo_manager_legacy_shutdown((struct radeon_bo_manager
*)bom
);
677 if (scrn
->sarea
->tiling_enabled
) {
678 bo
->base
.flags
= RADEON_BO_FLAGS_MACRO_TILE
;
681 bo
->offset
= bom
->screen
->backOffset
+ bom
->fb_location
;
682 bo
->base
.handle
= bo
->offset
;
683 bo
->ptr
= scrn
->driScreen
->pFB
+ bom
->screen
->backOffset
;
684 if (bo
->base
.handle
> bom
->nhandle
) {
685 bom
->nhandle
= bo
->base
.handle
+ 1;
688 bo
= bo_allocate(bom
, size
, 0, RADEON_GEM_DOMAIN_VRAM
, 0);
690 radeon_bo_manager_legacy_shutdown((struct radeon_bo_manager
*)bom
);
694 if (scrn
->sarea
->tiling_enabled
) {
695 bo
->base
.flags
|= RADEON_BO_FLAGS_MACRO_TILE
;
696 bo
->base
.flags
|= RADEON_BO_FLAGS_MICRO_TILE
;
699 bo
->offset
= bom
->screen
->depthOffset
+ bom
->fb_location
;
700 bo
->base
.handle
= bo
->offset
;
701 bo
->ptr
= scrn
->driScreen
->pFB
+ bom
->screen
->depthOffset
;
702 if (bo
->base
.handle
> bom
->nhandle
) {
703 bom
->nhandle
= bo
->base
.handle
+ 1;
705 return (struct radeon_bo_manager
*)bom
;
708 void radeon_bo_legacy_texture_age(struct radeon_bo_manager
*bom
)
710 struct bo_manager_legacy
*boml
= (struct bo_manager_legacy
*)bom
;
711 DRI_AGE_TEXTURES(boml
->texture_heap
);
714 unsigned radeon_bo_legacy_relocs_size(struct radeon_bo
*bo
)
716 struct bo_legacy
*bo_legacy
= (struct bo_legacy
*)bo
;
718 if (bo_legacy
->static_bo
|| (bo
->domains
& RADEON_GEM_DOMAIN_GTT
)) {