]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c
ENGR00240988: gpu: copy gpu-viv driver from 3.5.7 kernel
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_video_memory.c
1 /****************************************************************************
2 *
3 *    Copyright (C) 2005 - 2013 by Vivante Corp.
4 *
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.
9 *
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.
14 *
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.
18 *
19 *****************************************************************************/
20
21
22 #include "gc_hal_kernel_precomp.h"
23
24 #define _GC_OBJ_ZONE    gcvZONE_VIDMEM
25
26 /******************************************************************************\
27 ******************************* Private Functions ******************************
28 \******************************************************************************/
29
30 /*******************************************************************************
31 **
32 **  _Split
33 **
34 **  Split a node on the required byte boundary.
35 **
36 **  INPUT:
37 **
38 **      gckOS Os
39 **          Pointer to an gckOS object.
40 **
41 **      gcuVIDMEM_NODE_PTR Node
42 **          Pointer to the node to split.
43 **
44 **      gctSIZE_T Bytes
45 **          Number of bytes to keep in the node.
46 **
47 **  OUTPUT:
48 **
49 **      Nothing.
50 **
51 **  RETURNS:
52 **
53 **      gctBOOL
54 **          gcvTRUE if the node was split successfully, or gcvFALSE if there is an
55 **          error.
56 **
57 */
58 static gctBOOL
59 _Split(
60     IN gckOS Os,
61     IN gcuVIDMEM_NODE_PTR Node,
62     IN gctSIZE_T Bytes
63     )
64 {
65     gcuVIDMEM_NODE_PTR node;
66     gctPOINTER pointer = gcvNULL;
67
68     /* Make sure the byte boundary makes sense. */
69     if ((Bytes <= 0) || (Bytes > Node->VidMem.bytes))
70     {
71         return gcvFALSE;
72     }
73
74     /* Allocate a new gcuVIDMEM_NODE object. */
75     if (gcmIS_ERROR(gckOS_Allocate(Os,
76                                    gcmSIZEOF(gcuVIDMEM_NODE),
77                                    &pointer)))
78     {
79         /* Error. */
80         return gcvFALSE;
81     }
82
83     node = pointer;
84
85     /* Initialize gcuVIDMEM_NODE structure. */
86     node->VidMem.offset    = Node->VidMem.offset + Bytes;
87     node->VidMem.bytes     = Node->VidMem.bytes  - Bytes;
88     node->VidMem.alignment = 0;
89     node->VidMem.locked    = 0;
90     node->VidMem.memory    = Node->VidMem.memory;
91     node->VidMem.pool      = Node->VidMem.pool;
92     node->VidMem.physical  = Node->VidMem.physical;
93 #ifdef __QNXNTO__
94 #if gcdUSE_VIDMEM_PER_PID
95     gcmkASSERT(Node->VidMem.physical != 0);
96     gcmkASSERT(Node->VidMem.logical != gcvNULL);
97     node->VidMem.processID = Node->VidMem.processID;
98     node->VidMem.physical  = Node->VidMem.physical + Bytes;
99     node->VidMem.logical   = Node->VidMem.logical + Bytes;
100 #else
101     node->VidMem.processID = 0;
102     node->VidMem.logical   = gcvNULL;
103 #endif
104 #endif
105
106     /* Insert node behind specified node. */
107     node->VidMem.next = Node->VidMem.next;
108     node->VidMem.prev = Node;
109     Node->VidMem.next = node->VidMem.next->VidMem.prev = node;
110
111     /* Insert free node behind specified node. */
112     node->VidMem.nextFree = Node->VidMem.nextFree;
113     node->VidMem.prevFree = Node;
114     Node->VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node;
115
116     /* Adjust size of specified node. */
117     Node->VidMem.bytes = Bytes;
118
119     /* Success. */
120     return gcvTRUE;
121 }
122
123 /*******************************************************************************
124 **
125 **  _Merge
126 **
127 **  Merge two adjacent nodes together.
128 **
129 **  INPUT:
130 **
131 **      gckOS Os
132 **          Pointer to an gckOS object.
133 **
134 **      gcuVIDMEM_NODE_PTR Node
135 **          Pointer to the first of the two nodes to merge.
136 **
137 **  OUTPUT:
138 **
139 **      Nothing.
140 **
141 */
142 static gceSTATUS
143 _Merge(
144     IN gckOS Os,
145     IN gcuVIDMEM_NODE_PTR Node
146     )
147 {
148     gcuVIDMEM_NODE_PTR node;
149     gceSTATUS status;
150
151     /* Save pointer to next node. */
152     node = Node->VidMem.next;
153 #if gcdUSE_VIDMEM_PER_PID
154     /* Check if the nodes are adjacent physically. */
155     if ( ((Node->VidMem.physical + Node->VidMem.bytes) != node->VidMem.physical) ||
156           ((Node->VidMem.logical + Node->VidMem.bytes) != node->VidMem.logical) )
157     {
158         /* Can't merge. */
159         return gcvSTATUS_OK;
160     }
161 #else
162
163     /* This is a good time to make sure the heap is not corrupted. */
164     if (Node->VidMem.offset + Node->VidMem.bytes != node->VidMem.offset)
165     {
166         /* Corrupted heap. */
167         gcmkASSERT(
168             Node->VidMem.offset + Node->VidMem.bytes == node->VidMem.offset);
169         return gcvSTATUS_HEAP_CORRUPTED;
170     }
171 #endif
172
173     /* Adjust byte count. */
174     Node->VidMem.bytes += node->VidMem.bytes;
175
176     /* Unlink next node from linked list. */
177     Node->VidMem.next     = node->VidMem.next;
178     Node->VidMem.nextFree = node->VidMem.nextFree;
179
180     Node->VidMem.next->VidMem.prev         =
181     Node->VidMem.nextFree->VidMem.prevFree = Node;
182
183     /* Free next node. */
184     status = gcmkOS_SAFE_FREE(Os, node);
185     return status;
186 }
187
188 /******************************************************************************\
189 ******************************* gckVIDMEM API Code ******************************
190 \******************************************************************************/
191
192 /*******************************************************************************
193 **
194 **  gckVIDMEM_ConstructVirtual
195 **
196 **  Construct a new gcuVIDMEM_NODE union for virtual memory.
197 **
198 **  INPUT:
199 **
200 **      gckKERNEL Kernel
201 **          Pointer to an gckKERNEL object.
202 **
203 **      gctSIZE_T Bytes
204 **          Number of byte to allocate.
205 **
206 **  OUTPUT:
207 **
208 **      gcuVIDMEM_NODE_PTR * Node
209 **          Pointer to a variable that receives the gcuVIDMEM_NODE union pointer.
210 */
211 gceSTATUS
212 gckVIDMEM_ConstructVirtual(
213     IN gckKERNEL Kernel,
214     IN gctBOOL Contiguous,
215     IN gctSIZE_T Bytes,
216     OUT gcuVIDMEM_NODE_PTR * Node
217     )
218 {
219     gckOS os;
220     gceSTATUS status;
221     gcuVIDMEM_NODE_PTR node = gcvNULL;
222     gctPOINTER pointer = gcvNULL;
223     gctINT i;
224
225     gcmkHEADER_ARG("Kernel=0x%x Contiguous=%d Bytes=%lu", Kernel, Contiguous, Bytes);
226
227     /* Verify the arguments. */
228     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
229     gcmkVERIFY_ARGUMENT(Bytes > 0);
230     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
231
232     /* Extract the gckOS object pointer. */
233     os = Kernel->os;
234     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
235
236     /* Allocate an gcuVIDMEM_NODE union. */
237     gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer));
238
239     node = pointer;
240
241     /* Initialize gcuVIDMEM_NODE union for virtual memory. */
242     node->Virtual.kernel        = Kernel;
243     node->Virtual.contiguous    = Contiguous;
244     node->Virtual.logical       = gcvNULL;
245
246     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
247     {
248         node->Virtual.lockeds[i]        = 0;
249         node->Virtual.pageTables[i]     = gcvNULL;
250         node->Virtual.lockKernels[i]    = gcvNULL;
251     }
252
253     node->Virtual.mutex         = gcvNULL;
254
255     gcmkONERROR(gckOS_GetProcessID(&node->Virtual.processID));
256
257 #ifdef __QNXNTO__
258     node->Virtual.next          = gcvNULL;
259     node->Virtual.freePending   = gcvFALSE;
260     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
261     {
262         node->Virtual.unlockPendings[i] = gcvFALSE;
263     }
264 #endif
265
266     node->Virtual.freed         = gcvFALSE;
267
268     gcmkONERROR(gckOS_ZeroMemory(&node->Virtual.sharedInfo, gcmSIZEOF(gcsVIDMEM_NODE_SHARED_INFO)));
269
270     /* Create the mutex. */
271     gcmkONERROR(
272         gckOS_CreateMutex(os, &node->Virtual.mutex));
273
274     /* Allocate the virtual memory. */
275     gcmkONERROR(
276         gckOS_AllocatePagedMemoryEx(os,
277                                     node->Virtual.contiguous,
278                                     node->Virtual.bytes = Bytes,
279                                     &node->Virtual.physical));
280
281 #ifdef __QNXNTO__
282     /* Register. */
283 #if gcdENABLE_VG
284     if (Kernel->core != gcvCORE_VG)
285 #endif
286     {
287         gckMMU_InsertNode(Kernel->mmu, node);
288     }
289 #endif
290
291     /* Return pointer to the gcuVIDMEM_NODE union. */
292     *Node = node;
293
294     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
295                    "Created virtual node 0x%x for %u bytes @ 0x%x",
296                    node, Bytes, node->Virtual.physical);
297
298     /* Success. */
299     gcmkFOOTER_ARG("*Node=0x%x", *Node);
300     return gcvSTATUS_OK;
301
302 OnError:
303     /* Roll back. */
304     if (node != gcvNULL)
305     {
306         if (node->Virtual.mutex != gcvNULL)
307         {
308             /* Destroy the mutex. */
309             gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->Virtual.mutex));
310         }
311
312         /* Free the structure. */
313         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node));
314     }
315
316     /* Return the status. */
317     gcmkFOOTER();
318     return status;
319 }
320
321 /*******************************************************************************
322 **
323 **  gckVIDMEM_DestroyVirtual
324 **
325 **  Destroy an gcuVIDMEM_NODE union for virtual memory.
326 **
327 **  INPUT:
328 **
329 **      gcuVIDMEM_NODE_PTR Node
330 **          Pointer to a gcuVIDMEM_NODE union.
331 **
332 **  OUTPUT:
333 **
334 **      Nothing.
335 */
336 gceSTATUS
337 gckVIDMEM_DestroyVirtual(
338     IN gcuVIDMEM_NODE_PTR Node
339     )
340 {
341     gckOS os;
342     gctINT i;
343
344     gcmkHEADER_ARG("Node=0x%x", Node);
345
346     /* Verify the arguments. */
347     gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL);
348
349     /* Extact the gckOS object pointer. */
350     os = Node->Virtual.kernel->os;
351     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
352
353 #ifdef __QNXNTO__
354     /* Unregister. */
355 #if gcdENABLE_VG
356     if (Node->Virtual.kernel->core != gcvCORE_VG)
357 #endif
358     {
359         gcmkVERIFY_OK(
360                 gckMMU_RemoveNode(Node->Virtual.kernel->mmu, Node));
361     }
362 #endif
363
364     /* Delete the mutex. */
365     gcmkVERIFY_OK(gckOS_DeleteMutex(os, Node->Virtual.mutex));
366
367     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
368     {
369         if (Node->Virtual.pageTables[i] != gcvNULL)
370         {
371 #if gcdENABLE_VG
372             if (i == gcvCORE_VG)
373             {
374                 /* Free the pages. */
375                 gcmkVERIFY_OK(gckVGMMU_FreePages(Node->Virtual.lockKernels[i]->vg->mmu,
376                                                Node->Virtual.pageTables[i],
377                                                Node->Virtual.pageCount));
378             }
379             else
380 #endif
381             {
382                 /* Free the pages. */
383                 gcmkVERIFY_OK(gckMMU_FreePages(Node->Virtual.lockKernels[i]->mmu,
384                                                Node->Virtual.pageTables[i],
385                                                Node->Virtual.pageCount));
386             }
387         }
388     }
389
390     /* Delete the gcuVIDMEM_NODE union. */
391     gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, Node));
392
393     /* Success. */
394     gcmkFOOTER_NO();
395     return gcvSTATUS_OK;
396 }
397
398 /*******************************************************************************
399 **
400 **  gckVIDMEM_Construct
401 **
402 **  Construct a new gckVIDMEM object.
403 **
404 **  INPUT:
405 **
406 **      gckOS Os
407 **          Pointer to an gckOS object.
408 **
409 **      gctUINT32 BaseAddress
410 **          Base address for the video memory heap.
411 **
412 **      gctSIZE_T Bytes
413 **          Number of bytes in the video memory heap.
414 **
415 **      gctSIZE_T Threshold
416 **          Minimum number of bytes beyond am allocation before the node is
417 **          split.  Can be used as a minimum alignment requirement.
418 **
419 **      gctSIZE_T BankSize
420 **          Number of bytes per physical memory bank.  Used by bank
421 **          optimization.
422 **
423 **  OUTPUT:
424 **
425 **      gckVIDMEM * Memory
426 **          Pointer to a variable that will hold the pointer to the gckVIDMEM
427 **          object.
428 */
429 gceSTATUS
430 gckVIDMEM_Construct(
431     IN gckOS Os,
432     IN gctUINT32 BaseAddress,
433     IN gctSIZE_T Bytes,
434     IN gctSIZE_T Threshold,
435     IN gctSIZE_T BankSize,
436     OUT gckVIDMEM * Memory
437     )
438 {
439     gckVIDMEM memory = gcvNULL;
440     gceSTATUS status;
441     gcuVIDMEM_NODE_PTR node;
442     gctINT i, banks = 0;
443     gctPOINTER pointer = gcvNULL;
444
445     gcmkHEADER_ARG("Os=0x%x BaseAddress=%08x Bytes=%lu Threshold=%lu "
446                    "BankSize=%lu",
447                    Os, BaseAddress, Bytes, Threshold, BankSize);
448
449     /* Verify the arguments. */
450     gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
451     gcmkVERIFY_ARGUMENT(Bytes > 0);
452     gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
453
454     /* Allocate the gckVIDMEM object. */
455     gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct _gckVIDMEM), &pointer));
456
457     memory = pointer;
458
459     /* Initialize the gckVIDMEM object. */
460     memory->object.type = gcvOBJ_VIDMEM;
461     memory->os          = Os;
462
463     /* Set video memory heap information. */
464     memory->baseAddress = BaseAddress;
465     memory->bytes       = Bytes;
466     memory->freeBytes   = Bytes;
467     memory->threshold   = Threshold;
468     memory->mutex       = gcvNULL;
469 #if gcdUSE_VIDMEM_PER_PID
470     gcmkONERROR(gckOS_GetProcessID(&memory->pid));
471 #endif
472
473     BaseAddress = 0;
474
475     /* Walk all possible banks. */
476     for (i = 0; i < gcmCOUNTOF(memory->sentinel); ++i)
477     {
478         gctSIZE_T bytes;
479
480         if (BankSize == 0)
481         {
482             /* Use all bytes for the first bank. */
483             bytes = Bytes;
484         }
485         else
486         {
487             /* Compute number of bytes for this bank. */
488             bytes = gcmALIGN(BaseAddress + 1, BankSize) - BaseAddress;
489
490             if (bytes > Bytes)
491             {
492                 /* Make sure we don't exceed the total number of bytes. */
493                 bytes = Bytes;
494             }
495         }
496
497         if (bytes == 0)
498         {
499             /* Mark heap is not used. */
500             memory->sentinel[i].VidMem.next     =
501             memory->sentinel[i].VidMem.prev     =
502             memory->sentinel[i].VidMem.nextFree =
503             memory->sentinel[i].VidMem.prevFree = gcvNULL;
504             continue;
505         }
506
507         /* Allocate one gcuVIDMEM_NODE union. */
508         gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer));
509
510         node = pointer;
511
512         /* Initialize gcuVIDMEM_NODE union. */
513         node->VidMem.memory    = memory;
514
515         node->VidMem.next      =
516         node->VidMem.prev      =
517         node->VidMem.nextFree  =
518         node->VidMem.prevFree  = &memory->sentinel[i];
519
520         node->VidMem.offset    = BaseAddress;
521         node->VidMem.bytes     = bytes;
522         node->VidMem.alignment = 0;
523         node->VidMem.physical  = 0;
524         node->VidMem.pool      = gcvPOOL_UNKNOWN;
525
526         node->VidMem.locked    = 0;
527
528         gcmkONERROR(gckOS_ZeroMemory(&node->VidMem.sharedInfo, gcmSIZEOF(gcsVIDMEM_NODE_SHARED_INFO)));
529
530 #ifdef __QNXNTO__
531 #if gcdUSE_VIDMEM_PER_PID
532         node->VidMem.processID = memory->pid;
533         node->VidMem.physical  = memory->baseAddress + BaseAddress;
534         gcmkONERROR(gckOS_GetLogicalAddressProcess(Os,
535                     node->VidMem.processID,
536                     node->VidMem.physical,
537                     &node->VidMem.logical));
538 #else
539         node->VidMem.processID = 0;
540         node->VidMem.logical   = gcvNULL;
541 #endif
542 #endif
543
544         /* Initialize the linked list of nodes. */
545         memory->sentinel[i].VidMem.next     =
546         memory->sentinel[i].VidMem.prev     =
547         memory->sentinel[i].VidMem.nextFree =
548         memory->sentinel[i].VidMem.prevFree = node;
549
550         /* Mark sentinel. */
551         memory->sentinel[i].VidMem.bytes = 0;
552
553         /* Adjust address for next bank. */
554         BaseAddress += bytes;
555         Bytes       -= bytes;
556         banks       ++;
557     }
558
559     /* Assign all the bank mappings. */
560     memory->mapping[gcvSURF_RENDER_TARGET]      = banks - 1;
561     memory->mapping[gcvSURF_BITMAP]             = banks - 1;
562     if (banks > 1) --banks;
563     memory->mapping[gcvSURF_DEPTH]              = banks - 1;
564     memory->mapping[gcvSURF_HIERARCHICAL_DEPTH] = banks - 1;
565     if (banks > 1) --banks;
566     memory->mapping[gcvSURF_TEXTURE]            = banks - 1;
567     if (banks > 1) --banks;
568     memory->mapping[gcvSURF_VERTEX]             = banks - 1;
569     if (banks > 1) --banks;
570     memory->mapping[gcvSURF_INDEX]              = banks - 1;
571     if (banks > 1) --banks;
572     memory->mapping[gcvSURF_TILE_STATUS]        = banks - 1;
573     if (banks > 1) --banks;
574     memory->mapping[gcvSURF_TYPE_UNKNOWN]       = 0;
575
576 #if gcdENABLE_VG
577     memory->mapping[gcvSURF_IMAGE]   = 0;
578     memory->mapping[gcvSURF_MASK]    = 0;
579     memory->mapping[gcvSURF_SCISSOR] = 0;
580 #endif
581
582     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
583                   "[GALCORE] INDEX:         bank %d",
584                   memory->mapping[gcvSURF_INDEX]);
585     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
586                   "[GALCORE] VERTEX:        bank %d",
587                   memory->mapping[gcvSURF_VERTEX]);
588     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
589                   "[GALCORE] TEXTURE:       bank %d",
590                   memory->mapping[gcvSURF_TEXTURE]);
591     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
592                   "[GALCORE] RENDER_TARGET: bank %d",
593                   memory->mapping[gcvSURF_RENDER_TARGET]);
594     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
595                   "[GALCORE] DEPTH:         bank %d",
596                   memory->mapping[gcvSURF_DEPTH]);
597     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
598                   "[GALCORE] TILE_STATUS:   bank %d",
599                   memory->mapping[gcvSURF_TILE_STATUS]);
600
601     /* Allocate the mutex. */
602     gcmkONERROR(gckOS_CreateMutex(Os, &memory->mutex));
603
604     /* Return pointer to the gckVIDMEM object. */
605     *Memory = memory;
606
607     /* Success. */
608     gcmkFOOTER_ARG("*Memory=0x%x", *Memory);
609     return gcvSTATUS_OK;
610
611 OnError:
612     /* Roll back. */
613     if (memory != gcvNULL)
614     {
615         if (memory->mutex != gcvNULL)
616         {
617             /* Delete the mutex. */
618             gcmkVERIFY_OK(gckOS_DeleteMutex(Os, memory->mutex));
619         }
620
621         for (i = 0; i < banks; ++i)
622         {
623             /* Free the heap. */
624             gcmkASSERT(memory->sentinel[i].VidMem.next != gcvNULL);
625             gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory->sentinel[i].VidMem.next));
626         }
627
628         /* Free the object. */
629         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory));
630     }
631
632     /* Return the status. */
633     gcmkFOOTER();
634     return status;
635 }
636
637 /*******************************************************************************
638 **
639 **  gckVIDMEM_Destroy
640 **
641 **  Destroy an gckVIDMEM object.
642 **
643 **  INPUT:
644 **
645 **      gckVIDMEM Memory
646 **          Pointer to an gckVIDMEM object to destroy.
647 **
648 **  OUTPUT:
649 **
650 **      Nothing.
651 */
652 gceSTATUS
653 gckVIDMEM_Destroy(
654     IN gckVIDMEM Memory
655     )
656 {
657     gcuVIDMEM_NODE_PTR node, next;
658     gctINT i;
659
660     gcmkHEADER_ARG("Memory=0x%x", Memory);
661
662     /* Verify the arguments. */
663     gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
664
665     /* Walk all sentinels. */
666     for (i = 0; i < gcmCOUNTOF(Memory->sentinel); ++i)
667     {
668         /* Bail out of the heap is not used. */
669         if (Memory->sentinel[i].VidMem.next == gcvNULL)
670         {
671             break;
672         }
673
674         /* Walk all the nodes until we reach the sentinel. */
675         for (node = Memory->sentinel[i].VidMem.next;
676              node->VidMem.bytes != 0;
677              node = next)
678         {
679             /* Save pointer to the next node. */
680             next = node->VidMem.next;
681
682             /* Free the node. */
683             gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, node));
684         }
685     }
686
687     /* Free the mutex. */
688     gcmkVERIFY_OK(gckOS_DeleteMutex(Memory->os, Memory->mutex));
689
690     /* Mark the object as unknown. */
691     Memory->object.type = gcvOBJ_UNKNOWN;
692
693     /* Free the gckVIDMEM object. */
694     gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, Memory));
695
696     /* Success. */
697     gcmkFOOTER_NO();
698     return gcvSTATUS_OK;
699 }
700
701 /*******************************************************************************
702 **
703 **  gckVIDMEM_Allocate
704 **
705 **  Allocate rectangular memory from the gckVIDMEM object.
706 **
707 **  INPUT:
708 **
709 **      gckVIDMEM Memory
710 **          Pointer to an gckVIDMEM object.
711 **
712 **      gctUINT Width
713 **          Width of rectangle to allocate.  Make sure the width is properly
714 **          aligned.
715 **
716 **      gctUINT Height
717 **          Height of rectangle to allocate.  Make sure the height is properly
718 **          aligned.
719 **
720 **      gctUINT Depth
721 **          Depth of rectangle to allocate.  This equals to the number of
722 **          rectangles to allocate contiguously (i.e., for cubic maps and volume
723 **          textures).
724 **
725 **      gctUINT BytesPerPixel
726 **          Number of bytes per pixel.
727 **
728 **      gctUINT32 Alignment
729 **          Byte alignment for allocation.
730 **
731 **      gceSURF_TYPE Type
732 **          Type of surface to allocate (use by bank optimization).
733 **
734 **  OUTPUT:
735 **
736 **      gcuVIDMEM_NODE_PTR * Node
737 **          Pointer to a variable that will hold the allocated memory node.
738 */
739 gceSTATUS
740 gckVIDMEM_Allocate(
741     IN gckVIDMEM Memory,
742     IN gctUINT Width,
743     IN gctUINT Height,
744     IN gctUINT Depth,
745     IN gctUINT BytesPerPixel,
746     IN gctUINT32 Alignment,
747     IN gceSURF_TYPE Type,
748     OUT gcuVIDMEM_NODE_PTR * Node
749     )
750 {
751     gctSIZE_T bytes;
752     gceSTATUS status;
753
754     gcmkHEADER_ARG("Memory=0x%x Width=%u Height=%u Depth=%u BytesPerPixel=%u "
755                    "Alignment=%u Type=%d",
756                    Memory, Width, Height, Depth, BytesPerPixel, Alignment,
757                    Type);
758
759     /* Verify the arguments. */
760     gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
761     gcmkVERIFY_ARGUMENT(Width > 0);
762     gcmkVERIFY_ARGUMENT(Height > 0);
763     gcmkVERIFY_ARGUMENT(Depth > 0);
764     gcmkVERIFY_ARGUMENT(BytesPerPixel > 0);
765     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
766
767     /* Compute linear size. */
768     bytes = Width * Height * Depth * BytesPerPixel;
769
770     /* Allocate through linear function. */
771     gcmkONERROR(
772         gckVIDMEM_AllocateLinear(Memory, bytes, Alignment, Type, Node));
773
774     /* Success. */
775     gcmkFOOTER_ARG("*Node=0x%x", *Node);
776     return gcvSTATUS_OK;
777
778 OnError:
779     /* Return the status. */
780     gcmkFOOTER();
781     return status;
782 }
783
784 #if gcdENABLE_BANK_ALIGNMENT
785
786 #if !gcdBANK_BIT_START
787 #error gcdBANK_BIT_START not defined.
788 #endif
789
790 #if !gcdBANK_BIT_END
791 #error gcdBANK_BIT_END not defined.
792 #endif
793 /*******************************************************************************
794 **  _GetSurfaceBankAlignment
795 **
796 **  Return the required offset alignment required to the make BaseAddress
797 **  aligned properly.
798 **
799 **  INPUT:
800 **
801 **      gckOS Os
802 **          Pointer to gcoOS object.
803 **
804 **      gceSURF_TYPE Type
805 **          Type of allocation.
806 **
807 **      gctUINT32 BaseAddress
808 **          Base address of current video memory node.
809 **
810 **  OUTPUT:
811 **
812 **      gctUINT32_PTR AlignmentOffset
813 **          Pointer to a variable that will hold the number of bytes to skip in
814 **          the current video memory node in order to make the alignment bank
815 **          aligned.
816 */
817 static gceSTATUS
818 _GetSurfaceBankAlignment(
819     IN gceSURF_TYPE Type,
820     IN gctUINT32 BaseAddress,
821     OUT gctUINT32_PTR AlignmentOffset
822     )
823 {
824     gctUINT32 bank;
825     /* To retrieve the bank. */
826     static const gctUINT32 bankMask = (0xFFFFFFFF << gcdBANK_BIT_START)
827                                     ^ (0xFFFFFFFF << (gcdBANK_BIT_END + 1));
828
829     /* To retrieve the bank and all the lower bytes. */
830     static const gctUINT32 byteMask = ~(0xFFFFFFFF << (gcdBANK_BIT_END + 1));
831
832     gcmkHEADER_ARG("Type=%d BaseAddress=0x%x ", Type, BaseAddress);
833
834     /* Verify the arguments. */
835     gcmkVERIFY_ARGUMENT(AlignmentOffset != gcvNULL);
836
837     switch (Type)
838     {
839     case gcvSURF_RENDER_TARGET:
840         bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START);
841
842         /* Align to the first bank. */
843         *AlignmentOffset = (bank == 0) ?
844             0 :
845             ((1 << (gcdBANK_BIT_END + 1)) + 0) -  (BaseAddress & byteMask);
846         break;
847
848     case gcvSURF_DEPTH:
849         bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START);
850
851         /* Align to the third bank. */
852         *AlignmentOffset = (bank == 2) ?
853             0 :
854             ((1 << (gcdBANK_BIT_END + 1)) + (2 << gcdBANK_BIT_START)) -  (BaseAddress & byteMask);
855
856         /* Add a channel offset at the channel bit. */
857         *AlignmentOffset += (1 << gcdBANK_CHANNEL_BIT);
858         break;
859
860     default:
861         /* no alignment needed. */
862         *AlignmentOffset = 0;
863     }
864
865     /* Return the status. */
866     gcmkFOOTER_ARG("*AlignmentOffset=%u", *AlignmentOffset);
867     return gcvSTATUS_OK;
868 }
869 #endif
870
871 static gcuVIDMEM_NODE_PTR
872 _FindNode(
873     IN gckVIDMEM Memory,
874     IN gctINT Bank,
875     IN gctSIZE_T Bytes,
876     IN gceSURF_TYPE Type,
877     IN OUT gctUINT32_PTR Alignment
878     )
879 {
880     gcuVIDMEM_NODE_PTR node;
881     gctUINT32 alignment;
882
883 #if gcdENABLE_BANK_ALIGNMENT
884     gctUINT32 bankAlignment;
885     gceSTATUS status;
886 #endif
887
888     if (Memory->sentinel[Bank].VidMem.nextFree == gcvNULL)
889     {
890         /* No free nodes left. */
891         return gcvNULL;
892     }
893
894 #if gcdENABLE_BANK_ALIGNMENT
895     /* Walk all free nodes until we have one that is big enough or we have
896     ** reached the sentinel. */
897     for (node = Memory->sentinel[Bank].VidMem.nextFree;
898          node->VidMem.bytes != 0;
899          node = node->VidMem.nextFree)
900     {
901         gcmkONERROR(_GetSurfaceBankAlignment(
902             Type,
903             node->VidMem.memory->baseAddress + node->VidMem.offset,
904             &bankAlignment));
905
906         bankAlignment = gcmALIGN(bankAlignment, *Alignment);
907
908         /* Compute number of bytes to skip for alignment. */
909         alignment = (*Alignment == 0)
910                   ? 0
911                   : (*Alignment - (node->VidMem.offset % *Alignment));
912
913         if (alignment == *Alignment)
914         {
915             /* Node is already aligned. */
916             alignment = 0;
917         }
918
919         if (node->VidMem.bytes >= Bytes + alignment + bankAlignment)
920         {
921             /* This node is big enough. */
922             *Alignment = alignment + bankAlignment;
923             return node;
924         }
925     }
926 #endif
927
928     /* Walk all free nodes until we have one that is big enough or we have
929        reached the sentinel. */
930     for (node = Memory->sentinel[Bank].VidMem.nextFree;
931          node->VidMem.bytes != 0;
932          node = node->VidMem.nextFree)
933     {
934
935         gctINT modulo = gckMATH_ModuloInt(node->VidMem.offset, *Alignment);
936
937         /* Compute number of bytes to skip for alignment. */
938         alignment = (*Alignment == 0) ? 0 : (*Alignment - modulo);
939
940         if (alignment == *Alignment)
941         {
942             /* Node is already aligned. */
943             alignment = 0;
944         }
945
946         if (node->VidMem.bytes >= Bytes + alignment)
947         {
948             /* This node is big enough. */
949             *Alignment = alignment;
950             return node;
951         }
952     }
953
954 #if gcdENABLE_BANK_ALIGNMENT
955 OnError:
956 #endif
957     /* Not enough memory. */
958     return gcvNULL;
959 }
960
961 /*******************************************************************************
962 **
963 **  gckVIDMEM_AllocateLinear
964 **
965 **  Allocate linear memory from the gckVIDMEM object.
966 **
967 **  INPUT:
968 **
969 **      gckVIDMEM Memory
970 **          Pointer to an gckVIDMEM object.
971 **
972 **      gctSIZE_T Bytes
973 **          Number of bytes to allocate.
974 **
975 **      gctUINT32 Alignment
976 **          Byte alignment for allocation.
977 **
978 **      gceSURF_TYPE Type
979 **          Type of surface to allocate (use by bank optimization).
980 **
981 **  OUTPUT:
982 **
983 **      gcuVIDMEM_NODE_PTR * Node
984 **          Pointer to a variable that will hold the allocated memory node.
985 */
986 gceSTATUS
987 gckVIDMEM_AllocateLinear(
988     IN gckVIDMEM Memory,
989     IN gctSIZE_T Bytes,
990     IN gctUINT32 Alignment,
991     IN gceSURF_TYPE Type,
992     OUT gcuVIDMEM_NODE_PTR * Node
993     )
994 {
995     gceSTATUS status;
996     gcuVIDMEM_NODE_PTR node;
997     gctUINT32 alignment;
998     gctINT bank, i;
999     gctBOOL acquired = gcvFALSE;
1000
1001     gcmkHEADER_ARG("Memory=0x%x Bytes=%lu Alignment=%u Type=%d",
1002                    Memory, Bytes, Alignment, Type);
1003
1004     /* Verify the arguments. */
1005     gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
1006     gcmkVERIFY_ARGUMENT(Bytes > 0);
1007     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
1008     gcmkVERIFY_ARGUMENT(Type < gcvSURF_NUM_TYPES);
1009
1010     /* Acquire the mutex. */
1011     gcmkONERROR(gckOS_AcquireMutex(Memory->os, Memory->mutex, gcvINFINITE));
1012
1013     acquired = gcvTRUE;
1014 #if !gcdUSE_VIDMEM_PER_PID
1015
1016     if (Bytes > Memory->freeBytes)
1017     {
1018         /* Not enough memory. */
1019         status = gcvSTATUS_OUT_OF_MEMORY;
1020         goto OnError;
1021     }
1022 #endif
1023
1024 #if gcdSMALL_BLOCK_SIZE
1025     if ((Memory->freeBytes < (Memory->bytes/gcdRATIO_FOR_SMALL_MEMORY))
1026     &&  (Bytes >= gcdSMALL_BLOCK_SIZE)
1027     )
1028     {
1029         /* The left memory is for small memory.*/
1030         status = gcvSTATUS_OUT_OF_MEMORY;
1031         goto OnError;
1032     }
1033 #endif
1034
1035     /* Find the default bank for this surface type. */
1036     gcmkASSERT((gctINT) Type < gcmCOUNTOF(Memory->mapping));
1037     bank      = Memory->mapping[Type];
1038     alignment = Alignment;
1039
1040 #if gcdUSE_VIDMEM_PER_PID
1041     if (Bytes <= Memory->freeBytes)
1042     {
1043 #endif
1044     /* Find a free node in the default bank. */
1045     node = _FindNode(Memory, bank, Bytes, Type, &alignment);
1046
1047     /* Out of memory? */
1048     if (node == gcvNULL)
1049     {
1050         /* Walk all lower banks. */
1051         for (i = bank - 1; i >= 0; --i)
1052         {
1053             /* Find a free node inside the current bank. */
1054             node = _FindNode(Memory, i, Bytes, Type, &alignment);
1055             if (node != gcvNULL)
1056             {
1057                 break;
1058             }
1059         }
1060     }
1061
1062     if (node == gcvNULL)
1063     {
1064         /* Walk all upper banks. */
1065         for (i = bank + 1; i < gcmCOUNTOF(Memory->sentinel); ++i)
1066         {
1067             if (Memory->sentinel[i].VidMem.nextFree == gcvNULL)
1068             {
1069                 /* Abort when we reach unused banks. */
1070                 break;
1071             }
1072
1073             /* Find a free node inside the current bank. */
1074             node = _FindNode(Memory, i, Bytes, Type, &alignment);
1075             if (node != gcvNULL)
1076             {
1077                 break;
1078             }
1079         }
1080     }
1081 #if gcdUSE_VIDMEM_PER_PID
1082     }
1083 #endif
1084
1085     if (node == gcvNULL)
1086     {
1087         /* Out of memory. */
1088 #if gcdUSE_VIDMEM_PER_PID
1089         /* Allocate more memory from shared pool. */
1090         gctSIZE_T bytes;
1091         gctPHYS_ADDR physical_temp;
1092         gctUINT32 physical;
1093         gctPOINTER logical;
1094
1095         bytes = gcmALIGN(Bytes, gcdUSE_VIDMEM_PER_PID_SIZE);
1096
1097         gcmkONERROR(gckOS_AllocateContiguous(Memory->os,
1098                 gcvTRUE,
1099                 &bytes,
1100                 &physical_temp,
1101                 &logical));
1102
1103         /* physical address is returned as 0 for user space. workaround. */
1104         if (physical_temp == gcvNULL)
1105         {
1106         gcmkONERROR(gckOS_GetPhysicalAddress(Memory->os, logical, &physical));
1107         }
1108
1109         /* Allocate one gcuVIDMEM_NODE union. */
1110         gcmkONERROR(
1111             gckOS_Allocate(Memory->os,
1112                            gcmSIZEOF(gcuVIDMEM_NODE),
1113                            (gctPOINTER *) &node));
1114
1115         /* Initialize gcuVIDMEM_NODE union. */
1116         node->VidMem.memory    = Memory;
1117
1118         node->VidMem.offset    = 0;
1119         node->VidMem.bytes     = bytes;
1120         node->VidMem.alignment = 0;
1121         node->VidMem.physical  = physical;
1122         node->VidMem.pool      = gcvPOOL_UNKNOWN;
1123
1124         node->VidMem.locked    = 0;
1125
1126 #ifdef __QNXNTO__
1127         gcmkONERROR(gckOS_GetProcessID(&node->VidMem.processID));
1128         node->VidMem.logical   = logical;
1129         gcmkASSERT(logical != gcvNULL);
1130 #endif
1131
1132         /* Insert node behind sentinel node. */
1133         node->VidMem.next = Memory->sentinel[bank].VidMem.next;
1134         node->VidMem.prev = &Memory->sentinel[bank];
1135         Memory->sentinel[bank].VidMem.next = node->VidMem.next->VidMem.prev = node;
1136
1137         /* Insert free node behind sentinel node. */
1138         node->VidMem.nextFree = Memory->sentinel[bank].VidMem.nextFree;
1139         node->VidMem.prevFree = &Memory->sentinel[bank];
1140         Memory->sentinel[bank].VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node;
1141
1142         Memory->freeBytes += bytes;
1143 #else
1144         status = gcvSTATUS_OUT_OF_MEMORY;
1145         goto OnError;
1146 #endif
1147     }
1148
1149     /* Do we have an alignment? */
1150     if (alignment > 0)
1151     {
1152         /* Split the node so it is aligned. */
1153         if (_Split(Memory->os, node, alignment))
1154         {
1155             /* Successful split, move to aligned node. */
1156             node = node->VidMem.next;
1157
1158             /* Remove alignment. */
1159             alignment = 0;
1160         }
1161     }
1162
1163     /* Do we have enough memory after the allocation to split it? */
1164     if (node->VidMem.bytes - Bytes > Memory->threshold)
1165     {
1166         /* Adjust the node size. */
1167         _Split(Memory->os, node, Bytes);
1168     }
1169
1170     /* Remove the node from the free list. */
1171     node->VidMem.prevFree->VidMem.nextFree = node->VidMem.nextFree;
1172     node->VidMem.nextFree->VidMem.prevFree = node->VidMem.prevFree;
1173     node->VidMem.nextFree                  =
1174     node->VidMem.prevFree                  = gcvNULL;
1175
1176     /* Fill in the information. */
1177     node->VidMem.alignment = alignment;
1178     node->VidMem.memory    = Memory;
1179 #ifdef __QNXNTO__
1180 #if !gcdUSE_VIDMEM_PER_PID
1181     node->VidMem.logical   = gcvNULL;
1182     gcmkONERROR(gckOS_GetProcessID(&node->VidMem.processID));
1183 #else
1184     gcmkASSERT(node->VidMem.logical != gcvNULL);
1185 #endif
1186 #endif
1187
1188     /* Adjust the number of free bytes. */
1189     Memory->freeBytes -= node->VidMem.bytes;
1190
1191     node->VidMem.freePending = gcvFALSE;
1192
1193 #if gcdDYNAMIC_MAP_RESERVED_MEMORY && gcdENABLE_VG
1194     node->VidMem.kernelVirtual = gcvNULL;
1195 #endif
1196
1197     /* Release the mutex. */
1198     gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
1199
1200     /* Return the pointer to the node. */
1201     *Node = node;
1202
1203     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1204                    "Allocated %u bytes @ 0x%x [0x%08X]",
1205                    node->VidMem.bytes, node, node->VidMem.offset);
1206
1207     /* Success. */
1208     gcmkFOOTER_ARG("*Node=0x%x", *Node);
1209     return gcvSTATUS_OK;
1210
1211 OnError:
1212     if (acquired)
1213     {
1214      /* Release the mutex. */
1215         gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
1216     }
1217
1218     /* Return the status. */
1219     gcmkFOOTER();
1220     return status;
1221 }
1222
1223 /*******************************************************************************
1224 **
1225 **  gckVIDMEM_Free
1226 **
1227 **  Free an allocated video memory node.
1228 **
1229 **  INPUT:
1230 **
1231 **      gcuVIDMEM_NODE_PTR Node
1232 **          Pointer to a gcuVIDMEM_NODE object.
1233 **
1234 **  OUTPUT:
1235 **
1236 **      Nothing.
1237 */
1238 gceSTATUS
1239 gckVIDMEM_Free(
1240     IN gcuVIDMEM_NODE_PTR Node
1241     )
1242 {
1243     gceSTATUS status;
1244     gckKERNEL kernel = gcvNULL;
1245     gckVIDMEM memory = gcvNULL;
1246     gcuVIDMEM_NODE_PTR node;
1247     gctBOOL mutexAcquired = gcvFALSE;
1248     gckOS os = gcvNULL;
1249     gctBOOL acquired = gcvFALSE;
1250     gctINT32 i, totalLocked;
1251
1252     gcmkHEADER_ARG("Node=0x%x", Node);
1253
1254     /* Verify the arguments. */
1255     if ((Node == gcvNULL)
1256     ||  (Node->VidMem.memory == gcvNULL)
1257     )
1258     {
1259         /* Invalid object. */
1260         gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
1261     }
1262
1263     /**************************** Video Memory ********************************/
1264
1265     if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1266     {
1267         if (Node->VidMem.locked > 0)
1268         {
1269             /* Client still has a lock, defer free op 'till when lock reaches 0. */
1270             Node->VidMem.freePending = gcvTRUE;
1271
1272             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1273                            "Node 0x%x is locked (%d)... deferring free.",
1274                            Node, Node->VidMem.locked);
1275
1276             gcmkFOOTER_NO();
1277             return gcvSTATUS_OK;
1278         }
1279
1280         /* Extract pointer to gckVIDMEM object owning the node. */
1281         memory = Node->VidMem.memory;
1282
1283         /* Acquire the mutex. */
1284         gcmkONERROR(
1285             gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE));
1286
1287         mutexAcquired = gcvTRUE;
1288
1289 #ifdef __QNXNTO__
1290 #if !gcdUSE_VIDMEM_PER_PID
1291         /* Reset. */
1292         Node->VidMem.processID = 0;
1293         Node->VidMem.logical = gcvNULL;
1294 #endif
1295
1296         /* Don't try to re-free an already freed node. */
1297         if ((Node->VidMem.nextFree == gcvNULL)
1298         &&  (Node->VidMem.prevFree == gcvNULL)
1299         )
1300 #endif
1301         {
1302 #if gcdDYNAMIC_MAP_RESERVED_MEMORY && gcdENABLE_VG
1303             if (Node->VidMem.kernelVirtual)
1304             {
1305                 gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1306                         "%s(%d) Unmap %x from kernel space.",
1307                         __FUNCTION__, __LINE__,
1308                         Node->VidMem.kernelVirtual);
1309
1310                 gcmkVERIFY_OK(
1311                     gckOS_UnmapPhysical(memory->os,
1312                                         Node->VidMem.kernelVirtual,
1313                                         Node->VidMem.bytes));
1314
1315                 Node->VidMem.kernelVirtual = gcvNULL;
1316             }
1317 #endif
1318
1319             /* Check if Node is already freed. */
1320             if (Node->VidMem.nextFree)
1321             {
1322                 /* Node is alread freed. */
1323                 gcmkONERROR(gcvSTATUS_INVALID_DATA);
1324             }
1325
1326             /* Update the number of free bytes. */
1327             memory->freeBytes += Node->VidMem.bytes;
1328
1329             /* Find the next free node. */
1330             for (node = Node->VidMem.next;
1331                  node != gcvNULL && node->VidMem.nextFree == gcvNULL;
1332                  node = node->VidMem.next) ;
1333
1334             /* Insert this node in the free list. */
1335             Node->VidMem.nextFree = node;
1336             Node->VidMem.prevFree = node->VidMem.prevFree;
1337
1338             Node->VidMem.prevFree->VidMem.nextFree =
1339             node->VidMem.prevFree                  = Node;
1340
1341             /* Is the next node a free node and not the sentinel? */
1342             if ((Node->VidMem.next == Node->VidMem.nextFree)
1343             &&  (Node->VidMem.next->VidMem.bytes != 0)
1344             )
1345             {
1346                 /* Merge this node with the next node. */
1347                 gcmkONERROR(_Merge(memory->os, node = Node));
1348                 gcmkASSERT(node->VidMem.nextFree != node);
1349                 gcmkASSERT(node->VidMem.prevFree != node);
1350             }
1351
1352             /* Is the previous node a free node and not the sentinel? */
1353             if ((Node->VidMem.prev == Node->VidMem.prevFree)
1354             &&  (Node->VidMem.prev->VidMem.bytes != 0)
1355             )
1356             {
1357                 /* Merge this node with the previous node. */
1358                 gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev));
1359                 gcmkASSERT(node->VidMem.nextFree != node);
1360                 gcmkASSERT(node->VidMem.prevFree != node);
1361             }
1362         }
1363
1364         /* Release the mutex. */
1365         gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));
1366
1367         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1368                        "Node 0x%x is freed.",
1369                        Node);
1370
1371         /* Success. */
1372         gcmkFOOTER_NO();
1373         return gcvSTATUS_OK;
1374     }
1375
1376     /*************************** Virtual Memory *******************************/
1377
1378     /* Get gckKERNEL object. */
1379     kernel = Node->Virtual.kernel;
1380
1381     /* Verify the gckKERNEL object pointer. */
1382     gcmkVERIFY_OBJECT(kernel, gcvOBJ_KERNEL);
1383
1384     /* Get the gckOS object pointer. */
1385     os = kernel->os;
1386     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
1387
1388     /* Grab the mutex. */
1389     gcmkONERROR(
1390         gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE));
1391
1392     acquired = gcvTRUE;
1393
1394     for (i = 0, totalLocked = 0; i < gcdMAX_GPU_COUNT; i++)
1395     {
1396         totalLocked += Node->Virtual.lockeds[i];
1397     }
1398
1399     if (totalLocked > 0)
1400     {
1401         gcmkTRACE_ZONE(gcvLEVEL_ERROR, gcvZONE_VIDMEM,
1402                        "gckVIDMEM_Free: Virtual node 0x%x is locked (%d)",
1403                        Node, totalLocked);
1404
1405         /* Set Flag */
1406         Node->Virtual.freed = gcvTRUE;
1407
1408         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
1409     }
1410     else
1411     {
1412         /* Free the virtual memory. */
1413         gcmkVERIFY_OK(gckOS_FreePagedMemory(kernel->os,
1414                                             Node->Virtual.physical,
1415                                             Node->Virtual.bytes));
1416
1417         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
1418
1419         /* Destroy the gcuVIDMEM_NODE union. */
1420         gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node));
1421     }
1422
1423     /* Success. */
1424     gcmkFOOTER_NO();
1425     return gcvSTATUS_OK;
1426
1427 OnError:
1428     if (mutexAcquired)
1429     {
1430         /* Release the mutex. */
1431         gcmkVERIFY_OK(gckOS_ReleaseMutex(
1432             memory->os, memory->mutex
1433             ));
1434     }
1435
1436     if (acquired)
1437     {
1438        gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
1439     }
1440
1441     /* Return the status. */
1442     gcmkFOOTER();
1443     return status;
1444 }
1445
1446
1447 #ifdef __QNXNTO__
1448 /*******************************************************************************
1449 **
1450 **  gcoVIDMEM_FreeHandleMemory
1451 **
1452 **  Free all allocated video memory nodes for a handle.
1453 **
1454 **  INPUT:
1455 **
1456 **      gcoVIDMEM Memory
1457 **          Pointer to an gcoVIDMEM object..
1458 **
1459 **  OUTPUT:
1460 **
1461 **      Nothing.
1462 */
1463 gceSTATUS
1464 gckVIDMEM_FreeHandleMemory(
1465     IN gckKERNEL Kernel,
1466     IN gckVIDMEM Memory,
1467     IN gctUINT32 Pid
1468     )
1469 {
1470     gceSTATUS status;
1471     gctBOOL mutex = gcvFALSE;
1472     gcuVIDMEM_NODE_PTR node;
1473     gctINT i;
1474     gctUINT32 nodeCount = 0, byteCount = 0;
1475     gctBOOL again;
1476
1477     gcmkHEADER_ARG("Kernel=0x%x, Memory=0x%x Pid=0x%u", Kernel, Memory, Pid);
1478
1479     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
1480     gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
1481
1482     gcmkONERROR(gckOS_AcquireMutex(Memory->os, Memory->mutex, gcvINFINITE));
1483     mutex = gcvTRUE;
1484
1485     /* Walk all sentinels. */
1486     for (i = 0; i < gcmCOUNTOF(Memory->sentinel); ++i)
1487     {
1488         /* Bail out of the heap if it is not used. */
1489         if (Memory->sentinel[i].VidMem.next == gcvNULL)
1490         {
1491             break;
1492         }
1493
1494         do
1495         {
1496             again = gcvFALSE;
1497
1498             /* Walk all the nodes until we reach the sentinel. */
1499             for (node = Memory->sentinel[i].VidMem.next;
1500                  node->VidMem.bytes != 0;
1501                  node = node->VidMem.next)
1502             {
1503                 /* Free the node if it was allocated by Handle. */
1504                 if (node->VidMem.processID == Pid)
1505                 {
1506                     /* Unlock video memory. */
1507                     while (node->VidMem.locked > 0)
1508                     {
1509                         gckVIDMEM_Unlock(Kernel, node, gcvSURF_TYPE_UNKNOWN, gcvNULL);
1510                     }
1511
1512                     nodeCount++;
1513                     byteCount += node->VidMem.bytes;
1514
1515                     /* Free video memory. */
1516                     gcmkVERIFY_OK(gckVIDMEM_Free(node));
1517
1518                     /*
1519                      * Freeing may cause a merge which will invalidate our iteration.
1520                      * Don't be clever, just restart.
1521                      */
1522                     again = gcvTRUE;
1523
1524                     break;
1525                 }
1526 #if gcdUSE_VIDMEM_PER_PID
1527                 else
1528                 {
1529                     gcmkASSERT(node->VidMem.processID == Pid);
1530                 }
1531 #endif
1532             }
1533         }
1534         while (again);
1535     }
1536
1537     gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
1538     gcmkFOOTER();
1539     return gcvSTATUS_OK;
1540
1541 OnError:
1542     if (mutex)
1543     {
1544         gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
1545     }
1546
1547     gcmkFOOTER();
1548     return status;
1549 }
1550 #endif
1551
1552 /*******************************************************************************
1553 **
1554 ** _NeedVirtualMapping
1555 **
1556 **  Whether setup GPU page table for video node.
1557 **
1558 **  INPUT:
1559 **      gckKERNEL Kernel
1560 **          Pointer to an gckKERNEL object.
1561 **
1562 **      gcuVIDMEM_NODE_PTR Node
1563 **          Pointer to a gcuVIDMEM_NODE union.
1564 **
1565 **      gceCORE  Core
1566 **          Id of current GPU.
1567 **
1568 **  OUTPUT:
1569 **      gctBOOL * NeedMapping
1570 **          A pointer hold the result whether Node should be mapping.
1571 */
1572 static gceSTATUS
1573 _NeedVirtualMapping(
1574     IN gckKERNEL Kernel,
1575     IN gceCORE  Core,
1576     IN gcuVIDMEM_NODE_PTR Node,
1577     OUT gctBOOL * NeedMapping
1578 )
1579 {
1580     gceSTATUS status;
1581     gctUINT32 phys;
1582     gctUINT32 end;
1583     gcePOOL pool;
1584     gctUINT32 offset;
1585
1586     gcmkHEADER_ARG("Node=0x%X", Node);
1587
1588     /* Verify the arguments. */
1589     gcmkVERIFY_ARGUMENT(Kernel != gcvNULL);
1590     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
1591     gcmkVERIFY_ARGUMENT(NeedMapping != gcvNULL);
1592     gcmkVERIFY_ARGUMENT(Core < gcdMAX_GPU_COUNT);
1593
1594     if (Node->Virtual.contiguous)
1595     {
1596 #if gcdENABLE_VG
1597         if (Core == gcvCORE_VG)
1598         {
1599             *NeedMapping = gcvFALSE;
1600         }
1601         else
1602 #endif
1603         {
1604             /* For cores which can't access all physical address. */
1605             gcmkONERROR(gckHARDWARE_ConvertLogical(Kernel->hardware,
1606                         Node->Virtual.logical,
1607                         &phys));
1608
1609             /* If part of region is belong to gcvPOOL_VIRTUAL,
1610             ** whole region has to be mapped. */
1611             end = phys + Node->Virtual.bytes - 1;
1612
1613             gcmkONERROR(gckHARDWARE_SplitMemory(
1614                         Kernel->hardware, end, &pool, &offset
1615                         ));
1616
1617             *NeedMapping = (pool == gcvPOOL_VIRTUAL);
1618         }
1619     }
1620     else
1621     {
1622         *NeedMapping = gcvTRUE;
1623     }
1624
1625     gcmkFOOTER_ARG("*NeedMapping=%d", *NeedMapping);
1626     return gcvSTATUS_OK;
1627
1628 OnError:
1629     gcmkFOOTER();
1630     return status;
1631 }
1632
1633 /*******************************************************************************
1634 **
1635 **  gckVIDMEM_Lock
1636 **
1637 **  Lock a video memory node and return its hardware specific address.
1638 **
1639 **  INPUT:
1640 **
1641 **      gckKERNEL Kernel
1642 **          Pointer to an gckKERNEL object.
1643 **
1644 **      gcuVIDMEM_NODE_PTR Node
1645 **          Pointer to a gcuVIDMEM_NODE union.
1646 **
1647 **  OUTPUT:
1648 **
1649 **      gctUINT32 * Address
1650 **          Pointer to a variable that will hold the hardware specific address.
1651 */
1652 gceSTATUS
1653 gckVIDMEM_Lock(
1654     IN gckKERNEL Kernel,
1655     IN gcuVIDMEM_NODE_PTR Node,
1656     IN gctBOOL Cacheable,
1657     OUT gctUINT32 * Address
1658     )
1659 {
1660     gceSTATUS status;
1661     gctBOOL acquired = gcvFALSE;
1662     gctBOOL locked = gcvFALSE;
1663     gckOS os = gcvNULL;
1664     gctBOOL needMapping;
1665     gctUINT32 baseAddress;
1666
1667     gcmkHEADER_ARG("Node=0x%x", Node);
1668
1669     /* Verify the arguments. */
1670     gcmkVERIFY_ARGUMENT(Address != gcvNULL);
1671
1672     if ((Node == gcvNULL)
1673     ||  (Node->VidMem.memory == gcvNULL)
1674     )
1675     {
1676         /* Invalid object. */
1677         gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
1678     }
1679
1680     /**************************** Video Memory ********************************/
1681
1682     if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1683     {
1684         if (Cacheable == gcvTRUE)
1685         {
1686             gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
1687         }
1688
1689         /* Increment the lock count. */
1690         Node->VidMem.locked ++;
1691
1692         /* Return the physical address of the node. */
1693 #if !gcdUSE_VIDMEM_PER_PID
1694         *Address = Node->VidMem.memory->baseAddress
1695                  + Node->VidMem.offset
1696                  + Node->VidMem.alignment;
1697 #else
1698         *Address = Node->VidMem.physical;
1699 #endif
1700
1701         /* Get hardware specific address. */
1702 #if gcdENABLE_VG
1703         if (Kernel->vg == gcvNULL)
1704 #endif
1705         {
1706             if (Kernel->hardware->mmuVersion == 0)
1707             {
1708                 /* Convert physical to GPU address for old mmu. */
1709                 gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &baseAddress));
1710                 gcmkASSERT(*Address > baseAddress);
1711                 *Address -= baseAddress;
1712             }
1713         }
1714
1715         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1716                       "Locked node 0x%x (%d) @ 0x%08X",
1717                       Node,
1718                       Node->VidMem.locked,
1719                       *Address);
1720     }
1721
1722     /*************************** Virtual Memory *******************************/
1723
1724     else
1725     {
1726         /* Verify the gckKERNEL object pointer. */
1727         gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL);
1728
1729         /* Extract the gckOS object pointer. */
1730         os = Node->Virtual.kernel->os;
1731         gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
1732
1733         /* Grab the mutex. */
1734         gcmkONERROR(gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE));
1735         acquired = gcvTRUE;
1736
1737         gcmkONERROR(
1738             gckOS_LockPages(os,
1739                             Node->Virtual.physical,
1740                             Node->Virtual.bytes,
1741                             Cacheable,
1742                             &Node->Virtual.logical,
1743                             &Node->Virtual.pageCount));
1744
1745         /* Increment the lock count. */
1746         if (Node->Virtual.lockeds[Kernel->core] ++ == 0)
1747         {
1748             /* Is this node pending for a final unlock? */
1749 #ifdef __QNXNTO__
1750             if (!Node->Virtual.contiguous && Node->Virtual.unlockPendings[Kernel->core])
1751             {
1752                 /* Make sure we have a page table. */
1753                 gcmkASSERT(Node->Virtual.pageTables[Kernel->core] != gcvNULL);
1754
1755                 /* Remove pending unlock. */
1756                 Node->Virtual.unlockPendings[Kernel->core] = gcvFALSE;
1757             }
1758
1759             /* First lock - create a page table. */
1760             gcmkASSERT(Node->Virtual.pageTables[Kernel->core] == gcvNULL);
1761
1762             /* Make sure we mark our node as not flushed. */
1763             Node->Virtual.unlockPendings[Kernel->core] = gcvFALSE;
1764 #endif
1765
1766             locked = gcvTRUE;
1767
1768             gcmkONERROR(_NeedVirtualMapping(Kernel, Kernel->core, Node, &needMapping));
1769
1770             if (needMapping == gcvFALSE)
1771             {
1772                 /* Get hardware specific address. */
1773 #if gcdENABLE_VG
1774                 if (Kernel->vg != gcvNULL)
1775                 {
1776                     gcmkONERROR(gckVGHARDWARE_ConvertLogical(Kernel->vg->hardware,
1777                                 Node->Virtual.logical,
1778                                 &Node->Virtual.addresses[Kernel->core]));
1779                 }
1780                 else
1781 #endif
1782                 {
1783                     gcmkONERROR(gckHARDWARE_ConvertLogical(Kernel->hardware,
1784                                 Node->Virtual.logical,
1785                                 &Node->Virtual.addresses[Kernel->core]));
1786                 }
1787             }
1788             else
1789             {
1790 #if gcdENABLE_VG
1791                 if (Kernel->vg != gcvNULL)
1792                 {
1793                     /* Allocate pages inside the MMU. */
1794                     gcmkONERROR(
1795                         gckVGMMU_AllocatePages(Kernel->vg->mmu,
1796                                              Node->Virtual.pageCount,
1797                                              &Node->Virtual.pageTables[Kernel->core],
1798                                              &Node->Virtual.addresses[Kernel->core]));
1799                 }
1800                 else
1801 #endif
1802                 {
1803                     /* Allocate pages inside the MMU. */
1804                     gcmkONERROR(
1805                         gckMMU_AllocatePages(Kernel->mmu,
1806                                              Node->Virtual.pageCount,
1807                                              &Node->Virtual.pageTables[Kernel->core],
1808                                              &Node->Virtual.addresses[Kernel->core]));
1809                 }
1810
1811                 Node->Virtual.lockKernels[Kernel->core] = Kernel;
1812
1813                 /* Map the pages. */
1814 #ifdef __QNXNTO__
1815                 gcmkONERROR(
1816                     gckOS_MapPagesEx(os,
1817                                      Kernel->core,
1818                                      Node->Virtual.physical,
1819                                      Node->Virtual.logical,
1820                                      Node->Virtual.pageCount,
1821                                      Node->Virtual.pageTables[Kernel->core]));
1822 #else
1823                 gcmkONERROR(
1824                     gckOS_MapPagesEx(os,
1825                                      Kernel->core,
1826                                      Node->Virtual.physical,
1827                                      Node->Virtual.pageCount,
1828                                      Node->Virtual.pageTables[Kernel->core]));
1829 #endif
1830
1831 #if gcdENABLE_VG
1832                 if (Kernel->core == gcvCORE_VG)
1833                 {
1834                     gcmkONERROR(gckVGMMU_Flush(Kernel->vg->mmu));
1835                 }
1836                 else
1837 #endif
1838                 {
1839                     gcmkONERROR(gckMMU_Flush(Kernel->mmu));
1840                 }
1841             }
1842             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1843                            "Mapped virtual node 0x%x to 0x%08X",
1844                            Node,
1845                            Node->Virtual.addresses[Kernel->core]);
1846         }
1847
1848         /* Return hardware address. */
1849         *Address = Node->Virtual.addresses[Kernel->core];
1850
1851         /* Release the mutex. */
1852         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
1853     }
1854
1855     /* Success. */
1856     gcmkFOOTER_ARG("*Address=%08x", *Address);
1857     return gcvSTATUS_OK;
1858
1859 OnError:
1860     if (locked)
1861     {
1862         if (Node->Virtual.pageTables[Kernel->core] != gcvNULL)
1863         {
1864 #if gcdENABLE_VG
1865             if (Kernel->vg != gcvNULL)
1866             {
1867                 /* Free the pages from the MMU. */
1868                 gcmkVERIFY_OK(
1869                     gckVGMMU_FreePages(Kernel->vg->mmu,
1870                                      Node->Virtual.pageTables[Kernel->core],
1871                                      Node->Virtual.pageCount));
1872             }
1873             else
1874 #endif
1875             {
1876                 /* Free the pages from the MMU. */
1877                 gcmkVERIFY_OK(
1878                     gckMMU_FreePages(Kernel->mmu,
1879                                      Node->Virtual.pageTables[Kernel->core],
1880                                      Node->Virtual.pageCount));
1881             }
1882             Node->Virtual.pageTables[Kernel->core]  = gcvNULL;
1883             Node->Virtual.lockKernels[Kernel->core] = gcvNULL;
1884         }
1885
1886         /* Unlock the pages. */
1887         gcmkVERIFY_OK(
1888             gckOS_UnlockPages(os,
1889                               Node->Virtual.physical,
1890                               Node->Virtual.bytes,
1891                               Node->Virtual.logical
1892                               ));
1893
1894         Node->Virtual.lockeds[Kernel->core]--;
1895     }
1896
1897     if (acquired)
1898     {
1899         /* Release the mutex. */
1900         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
1901     }
1902
1903     /* Return the status. */
1904     gcmkFOOTER();
1905     return status;
1906 }
1907
1908 /*******************************************************************************
1909 **
1910 **  gckVIDMEM_Unlock
1911 **
1912 **  Unlock a video memory node.
1913 **
1914 **  INPUT:
1915 **
1916 **      gckKERNEL Kernel
1917 **          Pointer to an gckKERNEL object.
1918 **
1919 **      gcuVIDMEM_NODE_PTR Node
1920 **          Pointer to a locked gcuVIDMEM_NODE union.
1921 **
1922 **      gceSURF_TYPE Type
1923 **          Type of surface to unlock.
1924 **
1925 **      gctBOOL * Asynchroneous
1926 **          Pointer to a variable specifying whether the surface should be
1927 **          unlocked asynchroneously or not.
1928 **
1929 **  OUTPUT:
1930 **
1931 **      gctBOOL * Asynchroneous
1932 **          Pointer to a variable receiving the number of bytes used in the
1933 **          command buffer specified by 'Commands'.  If gcvNULL, there is no
1934 **          command buffer.
1935 */
1936 gceSTATUS
1937 gckVIDMEM_Unlock(
1938     IN gckKERNEL Kernel,
1939     IN gcuVIDMEM_NODE_PTR Node,
1940     IN gceSURF_TYPE Type,
1941     IN OUT gctBOOL * Asynchroneous
1942     )
1943 {
1944     gceSTATUS status;
1945     gckHARDWARE hardware;
1946     gctPOINTER buffer;
1947     gctSIZE_T requested, bufferSize;
1948     gckCOMMAND command = gcvNULL;
1949     gceKERNEL_FLUSH flush;
1950     gckOS os = gcvNULL;
1951     gctBOOL acquired = gcvFALSE;
1952     gctBOOL commitEntered = gcvFALSE;
1953     gctINT32 i, totalLocked;
1954
1955     gcmkHEADER_ARG("Node=0x%x Type=%d *Asynchroneous=%d",
1956                    Node, Type, gcmOPT_VALUE(Asynchroneous));
1957
1958     /* Verify the arguments. */
1959     if ((Node == gcvNULL)
1960     ||  (Node->VidMem.memory == gcvNULL)
1961     )
1962     {
1963         /* Invalid object. */
1964         gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
1965     }
1966
1967     /**************************** Video Memory ********************************/
1968
1969     if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1970     {
1971         if (Node->VidMem.locked <= 0)
1972         {
1973             /* The surface was not locked. */
1974             status = gcvSTATUS_MEMORY_UNLOCKED;
1975             goto OnError;
1976         }
1977
1978         /* Decrement the lock count. */
1979         Node->VidMem.locked --;
1980
1981         if (Asynchroneous != gcvNULL)
1982         {
1983             /* No need for any events. */
1984             *Asynchroneous = gcvFALSE;
1985         }
1986
1987         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1988                       "Unlocked node 0x%x (%d)",
1989                       Node,
1990                       Node->VidMem.locked);
1991
1992 #ifdef __QNXNTO__
1993         /* Unmap the video memory */
1994         if ((Node->VidMem.locked == 0) && (Node->VidMem.logical != gcvNULL))
1995         {
1996             if (Kernel->core == gcvCORE_VG)
1997             {
1998                 gckKERNEL_UnmapVideoMemory(Kernel,
1999                                            Node->VidMem.logical,
2000                                            Node->VidMem.processID,
2001                                            Node->VidMem.bytes);
2002                 Node->VidMem.logical = gcvNULL;
2003             }
2004         }
2005 #endif /* __QNXNTO__ */
2006
2007         if (Node->VidMem.freePending && (Node->VidMem.locked == 0))
2008         {
2009             /* Client has unlocked node previously attempted to be freed by compositor. Free now. */
2010             Node->VidMem.freePending = gcvFALSE;
2011             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
2012                            "Deferred-freeing Node 0x%x.",
2013                            Node);
2014             gcmkONERROR(gckVIDMEM_Free(Node));
2015         }
2016     }
2017
2018     /*************************** Virtual Memory *******************************/
2019
2020     else
2021     {
2022         /* Verify the gckHARDWARE object pointer. */
2023         hardware = Kernel->hardware;
2024         gcmkVERIFY_OBJECT(hardware, gcvOBJ_HARDWARE);
2025
2026         /* Verify the gckCOMMAND object pointer. */
2027         command = Kernel->command;
2028         gcmkVERIFY_OBJECT(command, gcvOBJ_COMMAND);
2029
2030         /* Get the gckOS object pointer. */
2031         os = Kernel->os;
2032         gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
2033
2034         /* Grab the mutex. */
2035         gcmkONERROR(
2036             gckOS_AcquireMutex(os, Node->Virtual.mutex, gcvINFINITE));
2037
2038         acquired = gcvTRUE;
2039
2040         if (Asynchroneous == gcvNULL)
2041         {
2042             if (Node->Virtual.lockeds[Kernel->core] == 0)
2043             {
2044                 status = gcvSTATUS_MEMORY_UNLOCKED;
2045                 goto OnError;
2046             }
2047
2048             /* Decrement lock count. */
2049             -- Node->Virtual.lockeds[Kernel->core];
2050
2051             /* See if we can unlock the resources. */
2052             if (Node->Virtual.lockeds[Kernel->core] == 0)
2053             {
2054                 /* Free the page table. */
2055                 if (Node->Virtual.pageTables[Kernel->core] != gcvNULL)
2056                 {
2057 #if gcdENABLE_VG
2058                     if (Kernel->vg != gcvNULL)
2059                     {
2060                         gcmkONERROR(
2061                             gckVGMMU_FreePages(Kernel->vg->mmu,
2062                                              Node->Virtual.pageTables[Kernel->core],
2063                                              Node->Virtual.pageCount));
2064                     }
2065                     else
2066 #endif
2067                     {
2068                         gcmkONERROR(
2069                             gckMMU_FreePages(Kernel->mmu,
2070                                              Node->Virtual.pageTables[Kernel->core],
2071                                              Node->Virtual.pageCount));
2072                     }
2073                     /* Mark page table as freed. */
2074                     Node->Virtual.pageTables[Kernel->core] = gcvNULL;
2075                     Node->Virtual.lockKernels[Kernel->core] = gcvNULL;
2076                 }
2077
2078 #ifdef __QNXNTO__
2079                 /* Mark node as unlocked. */
2080                 Node->Virtual.unlockPendings[Kernel->core] = gcvFALSE;
2081 #endif
2082             }
2083
2084             for (i = 0, totalLocked = 0; i < gcdMAX_GPU_COUNT; i++)
2085             {
2086                 totalLocked += Node->Virtual.lockeds[i];
2087             }
2088
2089             if (totalLocked == 0)
2090             {
2091                 /* Owner have already freed this node
2092                 ** and we are the last one to unlock, do
2093                 ** real free */
2094                 if (Node->Virtual.freed)
2095                 {
2096                     /* Free the virtual memory. */
2097                     gcmkVERIFY_OK(gckOS_FreePagedMemory(Kernel->os,
2098                                                         Node->Virtual.physical,
2099                                                         Node->Virtual.bytes));
2100
2101                     /* Release mutex before node is destroyed */
2102                     gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
2103
2104                     acquired = gcvFALSE;
2105
2106                     /* Destroy the gcuVIDMEM_NODE union. */
2107                     gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node));
2108
2109                     /* Node has been destroyed, so we should not touch it any more */
2110                     gcmkFOOTER();
2111                     return gcvSTATUS_OK;
2112                 }
2113             }
2114
2115             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
2116                            "Unmapped virtual node 0x%x from 0x%08X",
2117                            Node, Node->Virtual.addresses[Kernel->core]);
2118
2119         }
2120
2121         else
2122         {
2123             /* If we need to unlock a node from virtual memory we have to be
2124             ** very carefull.  If the node is still inside the caches we
2125             ** might get a bus error later if the cache line needs to be
2126             ** replaced.  So - we have to flush the caches before we do
2127             ** anything. */
2128
2129             /* gckCommand_EnterCommit() can't be called in interrupt handler because
2130             ** of a dead lock situation:
2131             ** process call Command_Commit(), and acquire Command->mutexQueue in
2132             ** gckCOMMAND_EnterCommit(). Then it will wait for a signal which depends
2133             ** on interrupt handler to generate, if interrupt handler enter
2134             ** gckCommand_EnterCommit(), process will never get the signal. */
2135
2136             /* So, flush cache when we still in process context, and then ask caller to
2137             ** schedule a event. */
2138
2139             gcmkONERROR(
2140                 gckOS_UnlockPages(os,
2141                               Node->Virtual.physical,
2142                               Node->Virtual.bytes,
2143                               Node->Virtual.logical));
2144
2145             if (!Node->Virtual.contiguous
2146             &&  (Node->Virtual.lockeds[Kernel->core] == 1)
2147 #if gcdENABLE_VG
2148             && (Kernel->vg == gcvNULL)
2149 #endif
2150             )
2151             {
2152                 if (Type == gcvSURF_BITMAP)
2153                 {
2154                     /* Flush 2D cache. */
2155                     flush = gcvFLUSH_2D;
2156                 }
2157                 else if (Type == gcvSURF_RENDER_TARGET)
2158                 {
2159                     /* Flush color cache. */
2160                     flush = gcvFLUSH_COLOR;
2161                 }
2162                 else if (Type == gcvSURF_DEPTH)
2163                 {
2164                     /* Flush depth cache. */
2165                     flush = gcvFLUSH_DEPTH;
2166                 }
2167                 else
2168                 {
2169                     /* No flush required. */
2170                     flush = (gceKERNEL_FLUSH) 0;
2171                 }
2172
2173                 gcmkONERROR(
2174                     gckHARDWARE_Flush(hardware, flush, gcvNULL, &requested));
2175
2176                 if (requested != 0)
2177                 {
2178                     /* Acquire the command queue. */
2179                     gcmkONERROR(gckCOMMAND_EnterCommit(command, gcvFALSE));
2180                     commitEntered = gcvTRUE;
2181
2182                     gcmkONERROR(gckCOMMAND_Reserve(
2183                         command, requested, &buffer, &bufferSize
2184                         ));
2185
2186                     gcmkONERROR(gckHARDWARE_Flush(
2187                         hardware, flush, buffer, &bufferSize
2188                         ));
2189
2190                     /* Mark node as pending. */
2191 #ifdef __QNXNTO__
2192                     Node->Virtual.unlockPendings[Kernel->core] = gcvTRUE;
2193 #endif
2194
2195                     gcmkONERROR(gckCOMMAND_Execute(command, requested));
2196
2197                     /* Release the command queue. */
2198                     gcmkONERROR(gckCOMMAND_ExitCommit(command, gcvFALSE));
2199                     commitEntered = gcvFALSE;
2200                 }
2201             }
2202
2203             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
2204                            "Scheduled unlock for virtual node 0x%x",
2205                            Node);
2206
2207             /* Schedule the surface to be unlocked. */
2208             *Asynchroneous = gcvTRUE;
2209         }
2210
2211         /* Release the mutex. */
2212         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
2213
2214         acquired = gcvFALSE;
2215     }
2216
2217     /* Success. */
2218     gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous));
2219     return gcvSTATUS_OK;
2220
2221 OnError:
2222     if (commitEntered)
2223     {
2224         /* Release the command queue mutex. */
2225         gcmkVERIFY_OK(gckCOMMAND_ExitCommit(command, gcvFALSE));
2226     }
2227
2228     if (acquired)
2229     {
2230         /* Release the mutex. */
2231         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->Virtual.mutex));
2232     }
2233
2234     /* Return the status. */
2235     gcmkFOOTER();
2236     return status;
2237 }