1 /****************************************************************************
3 * Copyright (C) 2005 - 2011 by Vivante Corp.
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the license, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *****************************************************************************/
24 #include "gc_hal_kernel_precomp.h"
26 #define _GC_OBJ_ZONE gcvZONE_VIDMEM
28 /******************************************************************************\
29 ******************************* Private Functions ******************************
30 \******************************************************************************/
32 /*******************************************************************************
36 ** Split a node on the required byte boundary.
41 ** Pointer to an gckOS object.
43 ** gcuVIDMEM_NODE_PTR Node
44 ** Pointer to the node to split.
47 ** Number of bytes to keep in the node.
56 ** gcvTRUE if the node was split successfully, or gcvFALSE if there is an
63 IN gcuVIDMEM_NODE_PTR Node
,
67 gcuVIDMEM_NODE_PTR node
;
68 gctPOINTER pointer
= gcvNULL
;
70 /* Make sure the byte boundary makes sense. */
71 if ((Bytes
<= 0) || (Bytes
> Node
->VidMem
.bytes
))
76 /* Allocate a new gcuVIDMEM_NODE object. */
77 if (gcmIS_ERROR(gckOS_Allocate(Os
,
78 gcmSIZEOF(gcuVIDMEM_NODE
),
87 /* Initialize gcuVIDMEM_NODE structure. */
88 node
->VidMem
.offset
= Node
->VidMem
.offset
+ Bytes
;
89 node
->VidMem
.bytes
= Node
->VidMem
.bytes
- Bytes
;
90 node
->VidMem
.alignment
= 0;
91 node
->VidMem
.locked
= 0;
92 node
->VidMem
.memory
= Node
->VidMem
.memory
;
93 node
->VidMem
.pool
= Node
->VidMem
.pool
;
94 node
->VidMem
.physical
= Node
->VidMem
.physical
;
96 #if gcdUSE_VIDMEM_PER_PID
97 gcmkASSERT(Node
->VidMem
.physical
!= 0);
98 gcmkASSERT(Node
->VidMem
.logical
!= gcvNULL
);
99 node
->VidMem
.processID
= Node
->VidMem
.processID
;
100 node
->VidMem
.physical
= Node
->VidMem
.physical
+ Bytes
;
101 node
->VidMem
.logical
= Node
->VidMem
.logical
+ Bytes
;
103 node
->VidMem
.processID
= 0;
104 node
->VidMem
.logical
= gcvNULL
;
108 /* Insert node behind specified node. */
109 node
->VidMem
.next
= Node
->VidMem
.next
;
110 node
->VidMem
.prev
= Node
;
111 Node
->VidMem
.next
= node
->VidMem
.next
->VidMem
.prev
= node
;
113 /* Insert free node behind specified node. */
114 node
->VidMem
.nextFree
= Node
->VidMem
.nextFree
;
115 node
->VidMem
.prevFree
= Node
;
116 Node
->VidMem
.nextFree
= node
->VidMem
.nextFree
->VidMem
.prevFree
= node
;
118 /* Adjust size of specified node. */
119 Node
->VidMem
.bytes
= Bytes
;
125 /*******************************************************************************
129 ** Merge two adjacent nodes together.
134 ** Pointer to an gckOS object.
136 ** gcuVIDMEM_NODE_PTR Node
137 ** Pointer to the first of the two nodes to merge.
147 IN gcuVIDMEM_NODE_PTR Node
150 gcuVIDMEM_NODE_PTR node
;
153 /* Save pointer to next node. */
154 node
= Node
->VidMem
.next
;
155 #if gcdUSE_VIDMEM_PER_PID
156 /* Check if the nodes are adjacent physically. */
157 if ( ((Node
->VidMem
.physical
+ Node
->VidMem
.bytes
) != node
->VidMem
.physical
) ||
158 ((Node
->VidMem
.logical
+ Node
->VidMem
.bytes
) != node
->VidMem
.logical
) )
165 /* This is a good time to make sure the heap is not corrupted. */
166 if (Node
->VidMem
.offset
+ Node
->VidMem
.bytes
!= node
->VidMem
.offset
)
168 /* Corrupted heap. */
170 Node
->VidMem
.offset
+ Node
->VidMem
.bytes
== node
->VidMem
.offset
);
171 return gcvSTATUS_HEAP_CORRUPTED
;
175 /* Adjust byte count. */
176 Node
->VidMem
.bytes
+= node
->VidMem
.bytes
;
178 /* Unlink next node from linked list. */
179 Node
->VidMem
.next
= node
->VidMem
.next
;
180 Node
->VidMem
.nextFree
= node
->VidMem
.nextFree
;
182 Node
->VidMem
.next
->VidMem
.prev
=
183 Node
->VidMem
.nextFree
->VidMem
.prevFree
= Node
;
185 /* Free next node. */
186 status
= gcmkOS_SAFE_FREE(Os
, node
);
190 /******************************************************************************\
191 ******************************* gckVIDMEM API Code ******************************
192 \******************************************************************************/
194 /*******************************************************************************
196 ** gckVIDMEM_ConstructVirtual
198 ** Construct a new gcuVIDMEM_NODE union for virtual memory.
203 ** Pointer to an gckKERNEL object.
206 ** Number of byte to allocate.
210 ** gcuVIDMEM_NODE_PTR * Node
211 ** Pointer to a variable that receives the gcuVIDMEM_NODE union pointer.
214 gckVIDMEM_ConstructVirtual(
216 IN gctBOOL Contiguous
,
218 OUT gcuVIDMEM_NODE_PTR
* Node
223 gcuVIDMEM_NODE_PTR node
= gcvNULL
;
224 gctPOINTER pointer
= gcvNULL
;
227 gcmkHEADER_ARG("Kernel=0x%x Contiguous=%d Bytes=%lu", Kernel
, Contiguous
, Bytes
);
229 /* Verify the arguments. */
230 gcmkVERIFY_OBJECT(Kernel
, gcvOBJ_KERNEL
);
231 gcmkVERIFY_ARGUMENT(Bytes
> 0);
232 gcmkVERIFY_ARGUMENT(Node
!= gcvNULL
);
234 /* Extract the gckOS object pointer. */
236 gcmkVERIFY_OBJECT(os
, gcvOBJ_OS
);
238 /* Allocate an gcuVIDMEM_NODE union. */
239 gcmkONERROR(gckOS_Allocate(os
, gcmSIZEOF(gcuVIDMEM_NODE
), &pointer
));
243 /* Initialize gcuVIDMEM_NODE union for virtual memory. */
244 node
->Virtual
.kernel
= Kernel
;
245 node
->Virtual
.contiguous
= Contiguous
;
246 node
->Virtual
.logical
= gcvNULL
;
248 for (i
= 0; i
< gcdCORE_COUNT
; i
++)
250 node
->Virtual
.lockeds
[i
] = 0;
251 node
->Virtual
.pageTables
[i
] = gcvNULL
;
252 node
->Virtual
.lockKernels
[i
] = gcvNULL
;
255 node
->Virtual
.mutex
= gcvNULL
;
257 gcmkONERROR(gckOS_GetProcessID(&node
->Virtual
.processID
));
260 node
->Virtual
.next
= gcvNULL
;
261 node
->Virtual
.freePending
= gcvFALSE
;
262 for (i
= 0; i
< gcdCORE_COUNT
; i
++)
264 node
->Virtual
.unlockPendings
[i
] = gcvFALSE
;
268 node
->Virtual
.freed
= gcvFALSE
;
269 /* Create the mutex. */
271 gckOS_CreateMutex(os
, &node
->Virtual
.mutex
));
273 /* Allocate the virtual memory. */
275 gckOS_AllocatePagedMemoryEx(os
,
276 node
->Virtual
.contiguous
,
277 node
->Virtual
.bytes
= Bytes
,
278 &node
->Virtual
.physical
));
282 gckMMU_InsertNode(Kernel
->mmu
, node
);
285 /* Return pointer to the gcuVIDMEM_NODE union. */
288 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
289 "Created virtual node 0x%x for %u bytes @ 0x%x",
290 node
, Bytes
, node
->Virtual
.physical
);
293 gcmkFOOTER_ARG("*Node=0x%x", *Node
);
300 if (node
->Virtual
.mutex
!= gcvNULL
)
302 /* Destroy the mutex. */
303 gcmkVERIFY_OK(gckOS_DeleteMutex(os
, node
->Virtual
.mutex
));
306 /* Free the structure. */
307 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os
, node
));
310 /* Return the status. */
315 /*******************************************************************************
317 ** gckVIDMEM_DestroyVirtual
319 ** Destroy an gcuVIDMEM_NODE union for virtual memory.
323 ** gcuVIDMEM_NODE_PTR Node
324 ** Pointer to a gcuVIDMEM_NODE union.
331 gckVIDMEM_DestroyVirtual(
332 IN gcuVIDMEM_NODE_PTR Node
338 gcmkHEADER_ARG("Node=0x%x", Node
);
340 /* Verify the arguments. */
341 gcmkVERIFY_OBJECT(Node
->Virtual
.kernel
, gcvOBJ_KERNEL
);
343 /* Extact the gckOS object pointer. */
344 os
= Node
->Virtual
.kernel
->os
;
345 gcmkVERIFY_OBJECT(os
, gcvOBJ_OS
);
350 gckMMU_RemoveNode(Node
->Virtual
.kernel
->mmu
, Node
));
353 /* Delete the mutex. */
354 gcmkVERIFY_OK(gckOS_DeleteMutex(os
, Node
->Virtual
.mutex
));
356 for (i
= 0; i
< gcdCORE_COUNT
; i
++)
358 if (Node
->Virtual
.pageTables
[i
] != gcvNULL
)
363 /* Free the pages. */
364 gcmkVERIFY_OK(gckVGMMU_FreePages(Node
->Virtual
.lockKernels
[i
]->vg
->mmu
,
365 Node
->Virtual
.pageTables
[i
],
366 Node
->Virtual
.pageCount
));
371 /* Free the pages. */
372 gcmkVERIFY_OK(gckMMU_FreePages(Node
->Virtual
.lockKernels
[i
]->mmu
,
373 Node
->Virtual
.pageTables
[i
],
374 Node
->Virtual
.pageCount
));
379 /* Delete the gcuVIDMEM_NODE union. */
380 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os
, Node
));
387 /*******************************************************************************
389 ** gckVIDMEM_Construct
391 ** Construct a new gckVIDMEM object.
396 ** Pointer to an gckOS object.
398 ** gctUINT32 BaseAddress
399 ** Base address for the video memory heap.
402 ** Number of bytes in the video memory heap.
404 ** gctSIZE_T Threshold
405 ** Minimum number of bytes beyond am allocation before the node is
406 ** split. Can be used as a minimum alignment requirement.
408 ** gctSIZE_T BankSize
409 ** Number of bytes per physical memory bank. Used by bank
414 ** gckVIDMEM * Memory
415 ** Pointer to a variable that will hold the pointer to the gckVIDMEM
421 IN gctUINT32 BaseAddress
,
423 IN gctSIZE_T Threshold
,
424 IN gctSIZE_T BankSize
,
425 OUT gckVIDMEM
* Memory
428 gckVIDMEM memory
= gcvNULL
;
430 gcuVIDMEM_NODE_PTR node
;
432 gctPOINTER pointer
= gcvNULL
;
434 gcmkHEADER_ARG("Os=0x%x BaseAddress=%08x Bytes=%lu Threshold=%lu "
436 Os
, BaseAddress
, Bytes
, Threshold
, BankSize
);
438 /* Verify the arguments. */
439 gcmkVERIFY_OBJECT(Os
, gcvOBJ_OS
);
440 gcmkVERIFY_ARGUMENT(Bytes
> 0);
441 gcmkVERIFY_ARGUMENT(Memory
!= gcvNULL
);
443 /* Allocate the gckVIDMEM object. */
444 gcmkONERROR(gckOS_Allocate(Os
, gcmSIZEOF(struct _gckVIDMEM
), &pointer
));
448 /* Initialize the gckVIDMEM object. */
449 memory
->object
.type
= gcvOBJ_VIDMEM
;
452 /* Set video memory heap information. */
453 memory
->baseAddress
= BaseAddress
;
454 memory
->bytes
= Bytes
;
455 memory
->freeBytes
= Bytes
;
456 memory
->threshold
= Threshold
;
457 memory
->mutex
= gcvNULL
;
458 #if gcdUSE_VIDMEM_PER_PID
459 gcmkONERROR(gckOS_GetProcessID(&memory
->pid
));
464 /* Walk all possible banks. */
465 for (i
= 0; i
< gcmCOUNTOF(memory
->sentinel
); ++i
)
471 /* Use all bytes for the first bank. */
476 /* Compute number of bytes for this bank. */
477 bytes
= gcmALIGN(BaseAddress
+ 1, BankSize
) - BaseAddress
;
481 /* Make sure we don't exceed the total number of bytes. */
488 /* Mark heap is not used. */
489 memory
->sentinel
[i
].VidMem
.next
=
490 memory
->sentinel
[i
].VidMem
.prev
=
491 memory
->sentinel
[i
].VidMem
.nextFree
=
492 memory
->sentinel
[i
].VidMem
.prevFree
= gcvNULL
;
496 /* Allocate one gcuVIDMEM_NODE union. */
497 gcmkONERROR(gckOS_Allocate(Os
, gcmSIZEOF(gcuVIDMEM_NODE
), &pointer
));
501 /* Initialize gcuVIDMEM_NODE union. */
502 node
->VidMem
.memory
= memory
;
506 node
->VidMem
.nextFree
=
507 node
->VidMem
.prevFree
= &memory
->sentinel
[i
];
509 node
->VidMem
.offset
= BaseAddress
;
510 node
->VidMem
.bytes
= bytes
;
511 node
->VidMem
.alignment
= 0;
512 node
->VidMem
.physical
= 0;
513 node
->VidMem
.pool
= gcvPOOL_UNKNOWN
;
515 node
->VidMem
.locked
= 0;
518 #if gcdUSE_VIDMEM_PER_PID
519 node
->VidMem
.processID
= memory
->pid
;
520 node
->VidMem
.physical
= memory
->baseAddress
+ BaseAddress
;
521 gcmkONERROR(gckOS_GetLogicalAddressProcess(Os
,
522 node
->VidMem
.processID
,
523 node
->VidMem
.physical
,
524 &node
->VidMem
.logical
));
526 node
->VidMem
.processID
= 0;
527 node
->VidMem
.logical
= gcvNULL
;
531 /* Initialize the linked list of nodes. */
532 memory
->sentinel
[i
].VidMem
.next
=
533 memory
->sentinel
[i
].VidMem
.prev
=
534 memory
->sentinel
[i
].VidMem
.nextFree
=
535 memory
->sentinel
[i
].VidMem
.prevFree
= node
;
538 memory
->sentinel
[i
].VidMem
.bytes
= 0;
540 /* Adjust address for next bank. */
541 BaseAddress
+= bytes
;
546 /* Assign all the bank mappings. */
547 memory
->mapping
[gcvSURF_RENDER_TARGET
] = banks
- 1;
548 memory
->mapping
[gcvSURF_BITMAP
] = banks
- 1;
549 if (banks
> 1) --banks
;
550 memory
->mapping
[gcvSURF_DEPTH
] = banks
- 1;
551 memory
->mapping
[gcvSURF_HIERARCHICAL_DEPTH
] = banks
- 1;
552 if (banks
> 1) --banks
;
553 memory
->mapping
[gcvSURF_TEXTURE
] = banks
- 1;
554 if (banks
> 1) --banks
;
555 memory
->mapping
[gcvSURF_VERTEX
] = banks
- 1;
556 if (banks
> 1) --banks
;
557 memory
->mapping
[gcvSURF_INDEX
] = banks
- 1;
558 if (banks
> 1) --banks
;
559 memory
->mapping
[gcvSURF_TILE_STATUS
] = banks
- 1;
560 if (banks
> 1) --banks
;
561 memory
->mapping
[gcvSURF_TYPE_UNKNOWN
] = 0;
564 memory
->mapping
[gcvSURF_IMAGE
] = 0;
565 memory
->mapping
[gcvSURF_MASK
] = 0;
566 memory
->mapping
[gcvSURF_SCISSOR
] = 0;
569 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
570 "[GALCORE] INDEX: bank %d",
571 memory
->mapping
[gcvSURF_INDEX
]);
572 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
573 "[GALCORE] VERTEX: bank %d",
574 memory
->mapping
[gcvSURF_VERTEX
]);
575 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
576 "[GALCORE] TEXTURE: bank %d",
577 memory
->mapping
[gcvSURF_TEXTURE
]);
578 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
579 "[GALCORE] RENDER_TARGET: bank %d",
580 memory
->mapping
[gcvSURF_RENDER_TARGET
]);
581 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
582 "[GALCORE] DEPTH: bank %d",
583 memory
->mapping
[gcvSURF_DEPTH
]);
584 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
585 "[GALCORE] TILE_STATUS: bank %d",
586 memory
->mapping
[gcvSURF_TILE_STATUS
]);
588 /* Allocate the mutex. */
589 gcmkONERROR(gckOS_CreateMutex(Os
, &memory
->mutex
));
591 /* Return pointer to the gckVIDMEM object. */
595 gcmkFOOTER_ARG("*Memory=0x%x", *Memory
);
600 if (memory
!= gcvNULL
)
602 if (memory
->mutex
!= gcvNULL
)
604 /* Delete the mutex. */
605 gcmkVERIFY_OK(gckOS_DeleteMutex(Os
, memory
->mutex
));
608 for (i
= 0; i
< banks
; ++i
)
611 gcmkASSERT(memory
->sentinel
[i
].VidMem
.next
!= gcvNULL
);
612 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os
, memory
->sentinel
[i
].VidMem
.next
));
615 /* Free the object. */
616 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os
, memory
));
619 /* Return the status. */
624 /*******************************************************************************
628 ** Destroy an gckVIDMEM object.
633 ** Pointer to an gckVIDMEM object to destroy.
644 gcuVIDMEM_NODE_PTR node
, next
;
647 gcmkHEADER_ARG("Memory=0x%x", Memory
);
649 /* Verify the arguments. */
650 gcmkVERIFY_OBJECT(Memory
, gcvOBJ_VIDMEM
);
652 /* Walk all sentinels. */
653 for (i
= 0; i
< gcmCOUNTOF(Memory
->sentinel
); ++i
)
655 /* Bail out of the heap is not used. */
656 if (Memory
->sentinel
[i
].VidMem
.next
== gcvNULL
)
661 /* Walk all the nodes until we reach the sentinel. */
662 for (node
= Memory
->sentinel
[i
].VidMem
.next
;
663 node
->VidMem
.bytes
!= 0;
666 /* Save pointer to the next node. */
667 next
= node
->VidMem
.next
;
670 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory
->os
, node
));
674 /* Free the mutex. */
675 gcmkVERIFY_OK(gckOS_DeleteMutex(Memory
->os
, Memory
->mutex
));
677 /* Mark the object as unknown. */
678 Memory
->object
.type
= gcvOBJ_UNKNOWN
;
680 /* Free the gckVIDMEM object. */
681 gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory
->os
, Memory
));
688 /*******************************************************************************
690 ** gckVIDMEM_Allocate
692 ** Allocate rectangular memory from the gckVIDMEM object.
697 ** Pointer to an gckVIDMEM object.
700 ** Width of rectangle to allocate. Make sure the width is properly
704 ** Height of rectangle to allocate. Make sure the height is properly
708 ** Depth of rectangle to allocate. This equals to the number of
709 ** rectangles to allocate contiguously (i.e., for cubic maps and volume
712 ** gctUINT BytesPerPixel
713 ** Number of bytes per pixel.
715 ** gctUINT32 Alignment
716 ** Byte alignment for allocation.
719 ** Type of surface to allocate (use by bank optimization).
723 ** gcuVIDMEM_NODE_PTR * Node
724 ** Pointer to a variable that will hold the allocated memory node.
732 IN gctUINT BytesPerPixel
,
733 IN gctUINT32 Alignment
,
734 IN gceSURF_TYPE Type
,
735 OUT gcuVIDMEM_NODE_PTR
* Node
741 gcmkHEADER_ARG("Memory=0x%x Width=%u Height=%u Depth=%u BytesPerPixel=%u "
742 "Alignment=%u Type=%d",
743 Memory
, Width
, Height
, Depth
, BytesPerPixel
, Alignment
,
746 /* Verify the arguments. */
747 gcmkVERIFY_OBJECT(Memory
, gcvOBJ_VIDMEM
);
748 gcmkVERIFY_ARGUMENT(Width
> 0);
749 gcmkVERIFY_ARGUMENT(Height
> 0);
750 gcmkVERIFY_ARGUMENT(Depth
> 0);
751 gcmkVERIFY_ARGUMENT(BytesPerPixel
> 0);
752 gcmkVERIFY_ARGUMENT(Node
!= gcvNULL
);
754 /* Compute linear size. */
755 bytes
= Width
* Height
* Depth
* BytesPerPixel
;
757 /* Allocate through linear function. */
759 gckVIDMEM_AllocateLinear(Memory
, bytes
, Alignment
, Type
, Node
));
762 gcmkFOOTER_ARG("*Node=0x%x", *Node
);
766 /* Return the status. */
771 static gcuVIDMEM_NODE_PTR
776 IN gceSURF_TYPE Type
,
777 IN OUT gctUINT32_PTR Alignment
780 gcuVIDMEM_NODE_PTR node
;
783 #if gcdENABLE_BANK_ALIGNMENT
784 gctUINT32 bankAlignment
;
787 /* Walk all free nodes until we have one that is big enough or we have
788 ** reached the sentinel. */
789 for (node
= Memory
->sentinel
[Bank
].VidMem
.nextFree
;
790 node
->VidMem
.bytes
!= 0;
791 node
= node
->VidMem
.nextFree
)
793 gcmkONERROR(gckOS_GetSurfaceBankAlignment(
796 node
->VidMem
.memory
->baseAddress
+ node
->VidMem
.offset
,
799 bankAlignment
= gcmALIGN(bankAlignment
, *Alignment
);
801 /* Compute number of bytes to skip for alignment. */
802 alignment
= (*Alignment
== 0)
804 : (*Alignment
- (node
->VidMem
.offset
% *Alignment
));
806 if (alignment
== *Alignment
)
808 /* Node is already aligned. */
812 if (node
->VidMem
.bytes
>= Bytes
+ alignment
+ bankAlignment
)
814 /* This node is big enough. */
815 *Alignment
= alignment
+ bankAlignment
;
821 /* Walk all free nodes until we have one that is big enough or we have
822 reached the sentinel. */
823 for (node
= Memory
->sentinel
[Bank
].VidMem
.nextFree
;
824 node
->VidMem
.bytes
!= 0;
825 node
= node
->VidMem
.nextFree
)
828 gctINT modulo
= gckMATH_ModuloInt(node
->VidMem
.offset
, *Alignment
);
830 /* Compute number of bytes to skip for alignment. */
831 alignment
= (*Alignment
== 0) ? 0 : (*Alignment
- modulo
);
833 if (alignment
== *Alignment
)
835 /* Node is already aligned. */
839 if (node
->VidMem
.bytes
>= Bytes
+ alignment
)
841 /* This node is big enough. */
842 *Alignment
= alignment
;
847 #if gcdENABLE_BANK_ALIGNMENT
850 /* Not enough memory. */
854 /*******************************************************************************
856 ** gckVIDMEM_AllocateLinear
858 ** Allocate linear memory from the gckVIDMEM object.
863 ** Pointer to an gckVIDMEM object.
866 ** Number of bytes to allocate.
868 ** gctUINT32 Alignment
869 ** Byte alignment for allocation.
872 ** Type of surface to allocate (use by bank optimization).
876 ** gcuVIDMEM_NODE_PTR * Node
877 ** Pointer to a variable that will hold the allocated memory node.
880 gckVIDMEM_AllocateLinear(
883 IN gctUINT32 Alignment
,
884 IN gceSURF_TYPE Type
,
885 OUT gcuVIDMEM_NODE_PTR
* Node
889 gcuVIDMEM_NODE_PTR node
;
892 gctBOOL acquired
= gcvFALSE
;
894 gcmkHEADER_ARG("Memory=0x%x Bytes=%lu Alignment=%u Type=%d",
895 Memory
, Bytes
, Alignment
, Type
);
897 /* Verify the arguments. */
898 gcmkVERIFY_OBJECT(Memory
, gcvOBJ_VIDMEM
);
899 gcmkVERIFY_ARGUMENT(Bytes
> 0);
900 gcmkVERIFY_ARGUMENT(Node
!= gcvNULL
);
901 gcmkVERIFY_ARGUMENT(Type
< gcvSURF_NUM_TYPES
);
903 /* Acquire the mutex. */
904 gcmkONERROR(gckOS_AcquireMutex(Memory
->os
, Memory
->mutex
, gcvINFINITE
));
907 #if !gcdUSE_VIDMEM_PER_PID
909 if (Bytes
> Memory
->freeBytes
)
911 /* Not enough memory. */
912 status
= gcvSTATUS_OUT_OF_MEMORY
;
917 /* Find the default bank for this surface type. */
918 gcmkASSERT((gctINT
) Type
< gcmCOUNTOF(Memory
->mapping
));
919 bank
= Memory
->mapping
[Type
];
920 alignment
= Alignment
;
922 #if gcdUSE_VIDMEM_PER_PID
923 if (Bytes
<= Memory
->freeBytes
)
926 /* Find a free node in the default bank. */
927 node
= _FindNode(Memory
, bank
, Bytes
, Type
, &alignment
);
932 /* Walk all lower banks. */
933 for (i
= bank
- 1; i
>= 0; --i
)
935 /* Find a free node inside the current bank. */
936 node
= _FindNode(Memory
, i
, Bytes
, Type
, &alignment
);
946 /* Walk all upper banks. */
947 for (i
= bank
+ 1; i
< gcmCOUNTOF(Memory
->sentinel
); ++i
)
949 if (Memory
->sentinel
[i
].VidMem
.nextFree
== gcvNULL
)
951 /* Abort when we reach unused banks. */
955 /* Find a free node inside the current bank. */
956 node
= _FindNode(Memory
, i
, Bytes
, Type
, &alignment
);
963 #if gcdUSE_VIDMEM_PER_PID
970 #if gcdUSE_VIDMEM_PER_PID
971 /* Allocate more memory from shared pool. */
973 gctPHYS_ADDR physical_temp
;
977 bytes
= gcmALIGN(Bytes
, gcdUSE_VIDMEM_PER_PID_SIZE
);
979 gcmkONERROR(gckOS_AllocateContiguous(Memory
->os
,
985 /* physical address is returned as 0 for user space. workaround. */
986 if (physical_temp
== gcvNULL
)
988 gcmkONERROR(gckOS_GetPhysicalAddress(Memory
->os
, logical
, &physical
));
991 /* Allocate one gcuVIDMEM_NODE union. */
993 gckOS_Allocate(Memory
->os
,
994 gcmSIZEOF(gcuVIDMEM_NODE
),
995 (gctPOINTER
*) &node
));
997 /* Initialize gcuVIDMEM_NODE union. */
998 node
->VidMem
.memory
= Memory
;
1000 node
->VidMem
.offset
= 0;
1001 node
->VidMem
.bytes
= bytes
;
1002 node
->VidMem
.alignment
= 0;
1003 node
->VidMem
.physical
= physical
;
1004 node
->VidMem
.pool
= gcvPOOL_UNKNOWN
;
1006 node
->VidMem
.locked
= 0;
1009 gcmkONERROR(gckOS_GetProcessID(&node
->VidMem
.processID
));
1010 node
->VidMem
.logical
= logical
;
1011 gcmkASSERT(logical
!= gcvNULL
);
1014 /* Insert node behind sentinel node. */
1015 node
->VidMem
.next
= Memory
->sentinel
[bank
].VidMem
.next
;
1016 node
->VidMem
.prev
= &Memory
->sentinel
[bank
];
1017 Memory
->sentinel
[bank
].VidMem
.next
= node
->VidMem
.next
->VidMem
.prev
= node
;
1019 /* Insert free node behind sentinel node. */
1020 node
->VidMem
.nextFree
= Memory
->sentinel
[bank
].VidMem
.nextFree
;
1021 node
->VidMem
.prevFree
= &Memory
->sentinel
[bank
];
1022 Memory
->sentinel
[bank
].VidMem
.nextFree
= node
->VidMem
.nextFree
->VidMem
.prevFree
= node
;
1024 Memory
->freeBytes
+= bytes
;
1026 status
= gcvSTATUS_OUT_OF_MEMORY
;
1031 /* Do we have an alignment? */
1034 /* Split the node so it is aligned. */
1035 if (_Split(Memory
->os
, node
, alignment
))
1037 /* Successful split, move to aligned node. */
1038 node
= node
->VidMem
.next
;
1040 /* Remove alignment. */
1045 /* Do we have enough memory after the allocation to split it? */
1046 if (node
->VidMem
.bytes
- Bytes
> Memory
->threshold
)
1048 /* Adjust the node size. */
1049 _Split(Memory
->os
, node
, Bytes
);
1052 /* Remove the node from the free list. */
1053 node
->VidMem
.prevFree
->VidMem
.nextFree
= node
->VidMem
.nextFree
;
1054 node
->VidMem
.nextFree
->VidMem
.prevFree
= node
->VidMem
.prevFree
;
1055 node
->VidMem
.nextFree
=
1056 node
->VidMem
.prevFree
= gcvNULL
;
1058 /* Fill in the information. */
1059 node
->VidMem
.alignment
= alignment
;
1060 node
->VidMem
.memory
= Memory
;
1062 #if !gcdUSE_VIDMEM_PER_PID
1063 node
->VidMem
.logical
= gcvNULL
;
1064 gcmkONERROR(gckOS_GetProcessID(&node
->VidMem
.processID
));
1066 gcmkASSERT(node
->VidMem
.logical
!= gcvNULL
);
1070 /* Adjust the number of free bytes. */
1071 Memory
->freeBytes
-= node
->VidMem
.bytes
;
1073 node
->VidMem
.freePending
= gcvFALSE
;
1075 /* Release the mutex. */
1076 gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory
->os
, Memory
->mutex
));
1078 /* Return the pointer to the node. */
1081 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1082 "Allocated %u bytes @ 0x%x [0x%08X]",
1083 node
->VidMem
.bytes
, node
, node
->VidMem
.offset
);
1086 gcmkFOOTER_ARG("*Node=0x%x", *Node
);
1087 return gcvSTATUS_OK
;
1092 /* Release the mutex. */
1093 gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory
->os
, Memory
->mutex
));
1096 /* Return the status. */
1101 /*******************************************************************************
1105 ** Free an allocated video memory node.
1109 ** gcuVIDMEM_NODE_PTR Node
1110 ** Pointer to a gcuVIDMEM_NODE object.
1118 IN gcuVIDMEM_NODE_PTR Node
1122 gckKERNEL kernel
= gcvNULL
;
1123 gckVIDMEM memory
= gcvNULL
;
1124 gcuVIDMEM_NODE_PTR node
;
1125 gctBOOL mutexAcquired
= gcvFALSE
;
1126 gckOS os
= gcvFALSE
;
1127 gctBOOL acquired
= gcvFALSE
;
1128 gctINT32 i
, totalLocked
;
1130 gcmkHEADER_ARG("Node=0x%x", Node
);
1132 /* Verify the arguments. */
1133 if ((Node
== gcvNULL
)
1134 || (Node
->VidMem
.memory
== gcvNULL
)
1137 /* Invalid object. */
1138 gcmkONERROR(gcvSTATUS_INVALID_OBJECT
);
1141 /**************************** Video Memory ********************************/
1143 if (Node
->VidMem
.memory
->object
.type
== gcvOBJ_VIDMEM
)
1145 if (Node
->VidMem
.locked
> 0)
1147 /* Client still has a lock, defer free op 'till when lock reaches 0. */
1148 Node
->VidMem
.freePending
= gcvTRUE
;
1150 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1151 "Node 0x%x is locked (%d)... deferring free.",
1152 Node
, Node
->VidMem
.locked
);
1155 return gcvSTATUS_OK
;
1158 /* Extract pointer to gckVIDMEM object owning the node. */
1159 memory
= Node
->VidMem
.memory
;
1161 /* Acquire the mutex. */
1163 gckOS_AcquireMutex(memory
->os
, memory
->mutex
, gcvINFINITE
));
1165 mutexAcquired
= gcvTRUE
;
1168 #if !gcdUSE_VIDMEM_PER_PID
1170 Node
->VidMem
.processID
= 0;
1171 Node
->VidMem
.logical
= gcvNULL
;
1174 /* Don't try to re-free an already freed node. */
1175 if ((Node
->VidMem
.nextFree
== gcvNULL
)
1176 && (Node
->VidMem
.prevFree
== gcvNULL
)
1180 /* Update the number of free bytes. */
1181 memory
->freeBytes
+= Node
->VidMem
.bytes
;
1183 /* Find the next free node. */
1184 for (node
= Node
->VidMem
.next
;
1185 node
!= gcvNULL
&& node
->VidMem
.nextFree
== gcvNULL
;
1186 node
= node
->VidMem
.next
) ;
1188 /* Insert this node in the free list. */
1189 Node
->VidMem
.nextFree
= node
;
1190 Node
->VidMem
.prevFree
= node
->VidMem
.prevFree
;
1192 Node
->VidMem
.prevFree
->VidMem
.nextFree
=
1193 node
->VidMem
.prevFree
= Node
;
1195 /* Is the next node a free node and not the sentinel? */
1196 if ((Node
->VidMem
.next
== Node
->VidMem
.nextFree
)
1197 && (Node
->VidMem
.next
->VidMem
.bytes
!= 0)
1200 /* Merge this node with the next node. */
1201 gcmkONERROR(_Merge(memory
->os
, node
= Node
));
1202 gcmkASSERT(node
->VidMem
.nextFree
!= node
);
1203 gcmkASSERT(node
->VidMem
.prevFree
!= node
);
1206 /* Is the previous node a free node and not the sentinel? */
1207 if ((Node
->VidMem
.prev
== Node
->VidMem
.prevFree
)
1208 && (Node
->VidMem
.prev
->VidMem
.bytes
!= 0)
1211 /* Merge this node with the previous node. */
1212 gcmkONERROR(_Merge(memory
->os
, node
= Node
->VidMem
.prev
));
1213 gcmkASSERT(node
->VidMem
.nextFree
!= node
);
1214 gcmkASSERT(node
->VidMem
.prevFree
!= node
);
1218 /* Release the mutex. */
1219 gcmkVERIFY_OK(gckOS_ReleaseMutex(memory
->os
, memory
->mutex
));
1221 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1222 "Node 0x%x is freed.",
1227 return gcvSTATUS_OK
;
1230 /*************************** Virtual Memory *******************************/
1232 /* Get gckKERNEL object. */
1233 kernel
= Node
->Virtual
.kernel
;
1235 /* Verify the gckKERNEL object pointer. */
1236 gcmkVERIFY_OBJECT(kernel
, gcvOBJ_KERNEL
);
1238 /* Get the gckOS object pointer. */
1240 gcmkVERIFY_OBJECT(os
, gcvOBJ_OS
);
1242 /* Grab the mutex. */
1244 gckOS_AcquireMutex(os
, Node
->Virtual
.mutex
, gcvINFINITE
));
1248 for (i
= 0, totalLocked
= 0; i
< gcdCORE_COUNT
; i
++)
1250 totalLocked
+= Node
->Virtual
.lockeds
[i
];
1253 if (totalLocked
> 0)
1255 gcmkTRACE_ZONE(gcvLEVEL_ERROR
, gcvZONE_VIDMEM
,
1256 "gckVIDMEM_Free: Virtual node 0x%x is locked (%d)",
1260 Node
->Virtual
.freed
= gcvTRUE
;
1262 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1266 /* Free the virtual memory. */
1267 gcmkVERIFY_OK(gckOS_FreePagedMemory(kernel
->os
,
1268 Node
->Virtual
.physical
,
1269 Node
->Virtual
.bytes
));
1271 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1273 /* Destroy the gcuVIDMEM_NODE union. */
1274 gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node
));
1279 return gcvSTATUS_OK
;
1284 /* Release the mutex. */
1285 gcmkVERIFY_OK(gckOS_ReleaseMutex(
1286 memory
->os
, memory
->mutex
1292 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1295 /* Return the status. */
1302 /*******************************************************************************
1304 ** gcoVIDMEM_FreeHandleMemory
1306 ** Free all allocated video memory nodes for a handle.
1311 ** Pointer to an gcoVIDMEM object..
1318 gckVIDMEM_FreeHandleMemory(
1319 IN gckKERNEL Kernel
,
1320 IN gckVIDMEM Memory
,
1325 gctBOOL mutex
= gcvFALSE
;
1326 gcuVIDMEM_NODE_PTR node
;
1328 gctUINT32 nodeCount
= 0, byteCount
= 0;
1331 gcmkHEADER_ARG("Kernel=0x%x, Memory=0x%x Pid=0x%u", Kernel
, Memory
, Pid
);
1333 gcmkVERIFY_OBJECT(Kernel
, gcvOBJ_KERNEL
);
1334 gcmkVERIFY_OBJECT(Memory
, gcvOBJ_VIDMEM
);
1336 gcmkONERROR(gckOS_AcquireMutex(Memory
->os
, Memory
->mutex
, gcvINFINITE
));
1339 /* Walk all sentinels. */
1340 for (i
= 0; i
< gcmCOUNTOF(Memory
->sentinel
); ++i
)
1342 /* Bail out of the heap if it is not used. */
1343 if (Memory
->sentinel
[i
].VidMem
.next
== gcvNULL
)
1352 /* Walk all the nodes until we reach the sentinel. */
1353 for (node
= Memory
->sentinel
[i
].VidMem
.next
;
1354 node
->VidMem
.bytes
!= 0;
1355 node
= node
->VidMem
.next
)
1357 /* Free the node if it was allocated by Handle. */
1358 if (node
->VidMem
.processID
== Pid
)
1360 /* Unlock video memory. */
1361 while (node
->VidMem
.locked
> 0)
1363 gckVIDMEM_Unlock(Kernel
, node
, gcvSURF_TYPE_UNKNOWN
, gcvNULL
);
1367 byteCount
+= node
->VidMem
.bytes
;
1369 /* Free video memory. */
1370 gcmkVERIFY_OK(gckVIDMEM_Free(node
));
1373 * Freeing may cause a merge which will invalidate our iteration.
1374 * Don't be clever, just restart.
1380 #if gcdUSE_VIDMEM_PER_PID
1383 gcmkASSERT(node
->VidMem
.processID
== Pid
);
1391 gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory
->os
, Memory
->mutex
));
1393 return gcvSTATUS_OK
;
1398 gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory
->os
, Memory
->mutex
));
1406 /*******************************************************************************
1410 ** Lock a video memory node and return its hardware specific address.
1415 ** Pointer to an gckKERNEL object.
1417 ** gcuVIDMEM_NODE_PTR Node
1418 ** Pointer to a gcuVIDMEM_NODE union.
1422 ** gctUINT32 * Address
1423 ** Pointer to a variable that will hold the hardware specific address.
1427 IN gckKERNEL Kernel
,
1428 IN gcuVIDMEM_NODE_PTR Node
,
1429 IN gctBOOL Cacheable
,
1430 OUT gctUINT32
* Address
1434 gctBOOL acquired
= gcvFALSE
;
1435 gctBOOL locked
= gcvFALSE
;
1438 gcmkHEADER_ARG("Node=0x%x", Node
);
1440 /* Verify the arguments. */
1441 gcmkVERIFY_ARGUMENT(Address
!= gcvNULL
);
1443 if ((Node
== gcvNULL
)
1444 || (Node
->VidMem
.memory
== gcvNULL
)
1447 /* Invalid object. */
1448 gcmkONERROR(gcvSTATUS_INVALID_OBJECT
);
1451 /**************************** Video Memory ********************************/
1453 if (Node
->VidMem
.memory
->object
.type
== gcvOBJ_VIDMEM
)
1455 if (Cacheable
== gcvTRUE
)
1457 gcmkONERROR(gcvSTATUS_INVALID_REQUEST
);
1460 /* Increment the lock count. */
1461 Node
->VidMem
.locked
++;
1463 /* Return the address of the node. */
1464 #if !gcdUSE_VIDMEM_PER_PID
1465 *Address
= Node
->VidMem
.memory
->baseAddress
1466 + Node
->VidMem
.offset
1467 + Node
->VidMem
.alignment
;
1469 *Address
= Node
->VidMem
.physical
;
1472 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1473 "Locked node 0x%x (%d) @ 0x%08X",
1475 Node
->VidMem
.locked
,
1479 /*************************** Virtual Memory *******************************/
1483 /* Verify the gckKERNEL object pointer. */
1484 gcmkVERIFY_OBJECT(Node
->Virtual
.kernel
, gcvOBJ_KERNEL
);
1486 /* Extract the gckOS object pointer. */
1487 os
= Node
->Virtual
.kernel
->os
;
1488 gcmkVERIFY_OBJECT(os
, gcvOBJ_OS
);
1490 /* Grab the mutex. */
1491 gcmkONERROR(gckOS_AcquireMutex(os
, Node
->Virtual
.mutex
, gcvINFINITE
));
1496 Node
->Virtual
.physical
,
1497 Node
->Virtual
.bytes
,
1499 &Node
->Virtual
.logical
,
1500 &Node
->Virtual
.pageCount
));
1502 /* Increment the lock count. */
1503 if (Node
->Virtual
.lockeds
[Kernel
->core
] ++ == 0)
1505 /* Is this node pending for a final unlock? */
1507 if (!Node
->Virtual
.contiguous
&& Node
->Virtual
.unlockPendings
[Kernel
->core
])
1509 /* Make sure we have a page table. */
1510 gcmkASSERT(Node
->Virtual
.pageTables
[Kernel
->core
] != gcvNULL
);
1512 /* Remove pending unlock. */
1513 Node
->Virtual
.unlockPendings
[Kernel
->core
] = gcvFALSE
;
1516 /* First lock - create a page table. */
1517 gcmkASSERT(Node
->Virtual
.pageTables
[Kernel
->core
] == gcvNULL
);
1519 /* Make sure we mark our node as not flushed. */
1520 Node
->Virtual
.unlockPendings
[Kernel
->core
] = gcvFALSE
;
1525 if (Node
->Virtual
.contiguous
)
1527 /* Get physical address directly */
1528 gcmkONERROR(gckOS_GetPhysicalAddress(os
,
1529 Node
->Virtual
.logical
,
1530 &Node
->Virtual
.addresses
[Kernel
->core
]));
1535 if (Kernel
->vg
!= gcvNULL
)
1537 /* Allocate pages inside the MMU. */
1539 gckVGMMU_AllocatePages(Kernel
->vg
->mmu
,
1540 Node
->Virtual
.pageCount
,
1541 &Node
->Virtual
.pageTables
[Kernel
->core
],
1542 &Node
->Virtual
.addresses
[Kernel
->core
]));
1547 /* Allocate pages inside the MMU. */
1549 gckMMU_AllocatePages(Kernel
->mmu
,
1550 Node
->Virtual
.pageCount
,
1551 &Node
->Virtual
.pageTables
[Kernel
->core
],
1552 &Node
->Virtual
.addresses
[Kernel
->core
]));
1555 Node
->Virtual
.lockKernels
[Kernel
->core
] = Kernel
;
1557 /* Map the pages. */
1560 gckOS_MapPagesEx(os
,
1562 Node
->Virtual
.physical
,
1563 Node
->Virtual
.logical
,
1564 Node
->Virtual
.pageCount
,
1565 Node
->Virtual
.pageTables
[Kernel
->core
]));
1568 gckOS_MapPagesEx(os
,
1570 Node
->Virtual
.physical
,
1571 Node
->Virtual
.pageCount
,
1572 Node
->Virtual
.pageTables
[Kernel
->core
]));
1576 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1577 "Mapped virtual node 0x%x to 0x%08X",
1579 Node
->Virtual
.addresses
[Kernel
->core
]);
1582 /* Return hardware address. */
1583 *Address
= Node
->Virtual
.addresses
[Kernel
->core
];
1585 /* Release the mutex. */
1586 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1590 gcmkFOOTER_ARG("*Address=%08x", *Address
);
1591 return gcvSTATUS_OK
;
1596 if (Node
->Virtual
.pageTables
[Kernel
->core
] != gcvNULL
)
1599 if (Kernel
->vg
!= gcvNULL
)
1601 /* Free the pages from the MMU. */
1603 gckVGMMU_FreePages(Kernel
->vg
->mmu
,
1604 Node
->Virtual
.pageTables
[Kernel
->core
],
1605 Node
->Virtual
.pageCount
));
1610 /* Free the pages from the MMU. */
1612 gckMMU_FreePages(Kernel
->mmu
,
1613 Node
->Virtual
.pageTables
[Kernel
->core
],
1614 Node
->Virtual
.pageCount
));
1616 Node
->Virtual
.pageTables
[Kernel
->core
] = gcvNULL
;
1617 Node
->Virtual
.lockKernels
[Kernel
->core
] = gcvNULL
;
1620 /* Unlock the pages. */
1622 gckOS_UnlockPages(os
,
1623 Node
->Virtual
.physical
,
1624 Node
->Virtual
.bytes
,
1625 Node
->Virtual
.logical
1628 Node
->Virtual
.lockeds
[Kernel
->core
]--;
1633 /* Release the mutex. */
1634 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1637 /* Return the status. */
1642 /*******************************************************************************
1646 ** Unlock a video memory node.
1651 ** Pointer to an gckKERNEL object.
1653 ** gcuVIDMEM_NODE_PTR Node
1654 ** Pointer to a locked gcuVIDMEM_NODE union.
1656 ** gceSURF_TYPE Type
1657 ** Type of surface to unlock.
1659 ** gctBOOL * Asynchroneous
1660 ** Pointer to a variable specifying whether the surface should be
1661 ** unlocked asynchroneously or not.
1665 ** gctBOOL * Asynchroneous
1666 ** Pointer to a variable receiving the number of bytes used in the
1667 ** command buffer specified by 'Commands'. If gcvNULL, there is no
1672 IN gckKERNEL Kernel
,
1673 IN gcuVIDMEM_NODE_PTR Node
,
1674 IN gceSURF_TYPE Type
,
1675 IN OUT gctBOOL
* Asynchroneous
1679 gckHARDWARE hardware
;
1681 gctSIZE_T requested
, bufferSize
;
1682 gckCOMMAND command
= gcvNULL
;
1683 gceKERNEL_FLUSH flush
;
1685 gctBOOL acquired
= gcvFALSE
;
1686 gctBOOL commitEntered
= gcvFALSE
;
1687 gctINT32 i
, totalLocked
;
1689 gcmkHEADER_ARG("Node=0x%x Type=%d *Asynchroneous=%d",
1690 Node
, Type
, gcmOPT_VALUE(Asynchroneous
));
1692 /* Verify the arguments. */
1693 if ((Node
== gcvNULL
)
1694 || (Node
->VidMem
.memory
== gcvNULL
)
1697 /* Invalid object. */
1698 gcmkONERROR(gcvSTATUS_INVALID_OBJECT
);
1701 /**************************** Video Memory ********************************/
1703 if (Node
->VidMem
.memory
->object
.type
== gcvOBJ_VIDMEM
)
1705 if (Node
->VidMem
.locked
<= 0)
1707 /* The surface was not locked. */
1708 status
= gcvSTATUS_MEMORY_UNLOCKED
;
1712 /* Decrement the lock count. */
1713 Node
->VidMem
.locked
--;
1715 if (Asynchroneous
!= gcvNULL
)
1717 /* No need for any events. */
1718 *Asynchroneous
= gcvFALSE
;
1721 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1722 "Unlocked node 0x%x (%d)",
1724 Node
->VidMem
.locked
);
1726 if (Node
->VidMem
.freePending
&& (Node
->VidMem
.locked
== 0))
1728 /* Client has unlocked node previously attempted to be freed by compositor. Free now. */
1729 Node
->VidMem
.freePending
= gcvFALSE
;
1730 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1731 "Deferred-freeing Node 0x%x.",
1733 gcmkONERROR(gckVIDMEM_Free(Node
));
1737 /*************************** Virtual Memory *******************************/
1741 /* Verify the gckHARDWARE object pointer. */
1742 hardware
= Kernel
->hardware
;
1743 gcmkVERIFY_OBJECT(hardware
, gcvOBJ_HARDWARE
);
1745 /* Verify the gckCOMMAND object pointer. */
1746 command
= Kernel
->command
;
1747 gcmkVERIFY_OBJECT(command
, gcvOBJ_COMMAND
);
1749 /* Get the gckOS object pointer. */
1751 gcmkVERIFY_OBJECT(os
, gcvOBJ_OS
);
1753 /* Grab the mutex. */
1755 gckOS_AcquireMutex(os
, Node
->Virtual
.mutex
, gcvINFINITE
));
1759 if (Asynchroneous
== gcvNULL
)
1761 if (Node
->Virtual
.lockeds
[Kernel
->core
] == 0)
1763 status
= gcvSTATUS_MEMORY_UNLOCKED
;
1767 /* Decrement lock count. */
1768 -- Node
->Virtual
.lockeds
[Kernel
->core
];
1770 /* See if we can unlock the resources. */
1771 if (Node
->Virtual
.lockeds
[Kernel
->core
] == 0)
1773 /* Free the page table. */
1774 if (Node
->Virtual
.pageTables
[Kernel
->core
] != gcvNULL
)
1777 if (Kernel
->vg
!= gcvNULL
)
1780 gckVGMMU_FreePages(Kernel
->vg
->mmu
,
1781 Node
->Virtual
.pageTables
[Kernel
->core
],
1782 Node
->Virtual
.pageCount
));
1788 gckMMU_FreePages(Kernel
->mmu
,
1789 Node
->Virtual
.pageTables
[Kernel
->core
],
1790 Node
->Virtual
.pageCount
));
1792 /* Mark page table as freed. */
1793 Node
->Virtual
.pageTables
[Kernel
->core
] = gcvNULL
;
1794 Node
->Virtual
.lockKernels
[Kernel
->core
] = gcvNULL
;
1798 /* Mark node as unlocked. */
1799 Node
->Virtual
.unlockPendings
[Kernel
->core
] = gcvFALSE
;
1803 for (i
= 0, totalLocked
= 0; i
< gcdCORE_COUNT
; i
++)
1805 totalLocked
+= Node
->Virtual
.lockeds
[i
];
1808 if (totalLocked
== 0)
1810 /* Owner have already freed this node
1811 ** and we are the last one to unlock, do
1813 if (Node
->Virtual
.freed
)
1815 /* Free the virtual memory. */
1816 gcmkVERIFY_OK(gckOS_FreePagedMemory(Kernel
->os
,
1817 Node
->Virtual
.physical
,
1818 Node
->Virtual
.bytes
));
1820 /* Release mutex before node is destroyed */
1821 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1823 acquired
= gcvFALSE
;
1825 /* Destroy the gcuVIDMEM_NODE union. */
1826 gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node
));
1828 /* Node has been destroyed, so we should not touch it any more */
1830 return gcvSTATUS_OK
;
1834 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1835 "Unmapped virtual node 0x%x from 0x%08X",
1836 Node
, Node
->Virtual
.addresses
[Kernel
->core
]);
1842 /* If we need to unlock a node from virtual memory we have to be
1843 ** very carefull. If the node is still inside the caches we
1844 ** might get a bus error later if the cache line needs to be
1845 ** replaced. So - we have to flush the caches before we do
1848 /* gckCommand_EnterCommit() can't be called in interrupt handler because
1849 ** of a dead lock situation:
1850 ** process call Command_Commit(), and acquire Command->mutexQueue in
1851 ** gckCOMMAND_EnterCommit(). Then it will wait for a signal which depends
1852 ** on interrupt handler to generate, if interrupt handler enter
1853 ** gckCommand_EnterCommit(), process will never get the signal. */
1855 /* So, flush cache when we still in process context, and then ask caller to
1856 ** schedule a event. */
1859 gckOS_UnlockPages(os
,
1860 Node
->Virtual
.physical
,
1861 Node
->Virtual
.bytes
,
1862 Node
->Virtual
.logical
));
1864 if (!Node
->Virtual
.contiguous
1865 && (Node
->Virtual
.lockeds
[Kernel
->core
] == 1)
1868 if (Type
== gcvSURF_BITMAP
)
1870 /* Flush 2D cache. */
1871 flush
= gcvFLUSH_2D
;
1873 else if (Type
== gcvSURF_RENDER_TARGET
)
1875 /* Flush color cache. */
1876 flush
= gcvFLUSH_COLOR
;
1878 else if (Type
== gcvSURF_DEPTH
)
1880 /* Flush depth cache. */
1881 flush
= gcvFLUSH_DEPTH
;
1885 /* No flush required. */
1886 flush
= (gceKERNEL_FLUSH
) 0;
1890 gckHARDWARE_Flush(hardware
, flush
, gcvNULL
, &requested
));
1894 /* Acquire the command queue. */
1895 gcmkONERROR(gckCOMMAND_EnterCommit(command
, gcvFALSE
));
1896 commitEntered
= gcvTRUE
;
1898 gcmkONERROR(gckCOMMAND_Reserve(
1899 command
, requested
, &buffer
, &bufferSize
1902 gcmkONERROR(gckHARDWARE_Flush(
1903 hardware
, flush
, buffer
, &bufferSize
1906 /* Mark node as pending. */
1908 Node
->Virtual
.unlockPendings
[Kernel
->core
] = gcvTRUE
;
1911 gcmkONERROR(gckCOMMAND_Execute(command
, requested
));
1913 /* Release the command queue. */
1914 gcmkONERROR(gckCOMMAND_ExitCommit(command
, gcvFALSE
));
1915 commitEntered
= gcvFALSE
;
1919 gcmkTRACE_ZONE(gcvLEVEL_INFO
, gcvZONE_VIDMEM
,
1920 "Scheduled unlock for virtual node 0x%x",
1923 /* Schedule the surface to be unlocked. */
1924 *Asynchroneous
= gcvTRUE
;
1927 /* Release the mutex. */
1928 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1930 acquired
= gcvFALSE
;
1934 gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous
));
1935 return gcvSTATUS_OK
;
1940 /* Release the command queue mutex. */
1941 gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command
, gcvFALSE
));
1946 /* Release the mutex. */
1947 gcmkVERIFY_OK(gckOS_ReleaseMutex(os
, Node
->Virtual
.mutex
));
1950 /* Return the status. */