]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - drivers/mxc/gpu-viv/hal/kernel/gc_hal_kernel_video_memory.c
gpu: vivante: Update driver from Freescale 3.10.53-1.1-ga BSP
[karo-tx-linux.git] / drivers / mxc / gpu-viv / hal / kernel / gc_hal_kernel_video_memory.c
1 /****************************************************************************
2 *
3 *    Copyright (C) 2005 - 2014 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     node->VidMem.processID = 0;
95     node->VidMem.logical   = gcvNULL;
96 #endif
97
98     /* Insert node behind specified node. */
99     node->VidMem.next = Node->VidMem.next;
100     node->VidMem.prev = Node;
101     Node->VidMem.next = node->VidMem.next->VidMem.prev = node;
102
103     /* Insert free node behind specified node. */
104     node->VidMem.nextFree = Node->VidMem.nextFree;
105     node->VidMem.prevFree = Node;
106     Node->VidMem.nextFree = node->VidMem.nextFree->VidMem.prevFree = node;
107
108     /* Adjust size of specified node. */
109     Node->VidMem.bytes = Bytes;
110
111     /* Success. */
112     return gcvTRUE;
113 }
114
115 /*******************************************************************************
116 **
117 **  _Merge
118 **
119 **  Merge two adjacent nodes together.
120 **
121 **  INPUT:
122 **
123 **      gckOS Os
124 **          Pointer to an gckOS object.
125 **
126 **      gcuVIDMEM_NODE_PTR Node
127 **          Pointer to the first of the two nodes to merge.
128 **
129 **  OUTPUT:
130 **
131 **      Nothing.
132 **
133 */
134 static gceSTATUS
135 _Merge(
136     IN gckOS Os,
137     IN gcuVIDMEM_NODE_PTR Node
138     )
139 {
140     gcuVIDMEM_NODE_PTR node;
141     gceSTATUS status;
142
143     /* Save pointer to next node. */
144     node = Node->VidMem.next;
145
146     /* This is a good time to make sure the heap is not corrupted. */
147     if (Node->VidMem.offset + Node->VidMem.bytes != node->VidMem.offset)
148     {
149         /* Corrupted heap. */
150         gcmkASSERT(
151             Node->VidMem.offset + Node->VidMem.bytes == node->VidMem.offset);
152         return gcvSTATUS_HEAP_CORRUPTED;
153     }
154
155     /* Adjust byte count. */
156     Node->VidMem.bytes += node->VidMem.bytes;
157
158     /* Unlink next node from linked list. */
159     Node->VidMem.next     = node->VidMem.next;
160     Node->VidMem.nextFree = node->VidMem.nextFree;
161
162     Node->VidMem.next->VidMem.prev         =
163     Node->VidMem.nextFree->VidMem.prevFree = Node;
164
165     /* Free next node. */
166     status = gcmkOS_SAFE_FREE(Os, node);
167     return status;
168 }
169
170 /******************************************************************************\
171 ******************************* gckVIDMEM API Code ******************************
172 \******************************************************************************/
173
174 /*******************************************************************************
175 **
176 **  gckVIDMEM_ConstructVirtual
177 **
178 **  Construct a new gcuVIDMEM_NODE union for virtual memory.
179 **
180 **  INPUT:
181 **
182 **      gckKERNEL Kernel
183 **          Pointer to an gckKERNEL object.
184 **
185 **      gctSIZE_T Bytes
186 **          Number of byte to allocate.
187 **
188 **  OUTPUT:
189 **
190 **      gcuVIDMEM_NODE_PTR * Node
191 **          Pointer to a variable that receives the gcuVIDMEM_NODE union pointer.
192 */
193 gceSTATUS
194 gckVIDMEM_ConstructVirtual(
195     IN gckKERNEL Kernel,
196     IN gctUINT32 Flag,
197     IN gctSIZE_T Bytes,
198     OUT gcuVIDMEM_NODE_PTR * Node
199     )
200 {
201     gckOS os;
202     gceSTATUS status;
203     gcuVIDMEM_NODE_PTR node = gcvNULL;
204     gctPOINTER pointer = gcvNULL;
205     gctINT i;
206
207     gcmkHEADER_ARG("Kernel=0x%x Flag=%x Bytes=%lu", Kernel, Flag, Bytes);
208
209     /* Verify the arguments. */
210     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
211     gcmkVERIFY_ARGUMENT(Bytes > 0);
212     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
213
214     /* Extract the gckOS object pointer. */
215     os = Kernel->os;
216     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
217
218     /* Allocate an gcuVIDMEM_NODE union. */
219     gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer));
220
221     node = pointer;
222
223     /* Initialize gcuVIDMEM_NODE union for virtual memory. */
224     node->Virtual.kernel        = Kernel;
225     node->Virtual.contiguous    = Flag & gcvALLOC_FLAG_CONTIGUOUS;
226     node->Virtual.logical       = gcvNULL;
227 #if gcdENABLE_VG
228     node->Virtual.kernelVirtual = gcvNULL;
229 #endif
230
231     for (i = 0; i < gcdMAX_GPU_COUNT; i++)
232     {
233         node->Virtual.lockeds[i]        = 0;
234         node->Virtual.pageTables[i]     = gcvNULL;
235         node->Virtual.lockKernels[i]    = gcvNULL;
236     }
237
238     gcmkONERROR(gckOS_GetProcessID(&node->Virtual.processID));
239
240     /* Allocate the virtual memory. */
241     gcmkONERROR(
242         gckOS_AllocatePagedMemoryEx(os,
243                                     Flag,
244                                     node->Virtual.bytes = Bytes,
245                                     &node->Virtual.gid,
246                                     &node->Virtual.physical));
247
248     /* Return pointer to the gcuVIDMEM_NODE union. */
249     *Node = node;
250
251     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
252                    "Created virtual node 0x%x for %u bytes @ 0x%x",
253                    node, Bytes, node->Virtual.physical);
254
255     /* Success. */
256     gcmkFOOTER_ARG("*Node=0x%x", *Node);
257     return gcvSTATUS_OK;
258
259 OnError:
260     /* Roll back. */
261     if (node != gcvNULL)
262     {
263         /* Free the structure. */
264         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node));
265     }
266
267     /* Return the status. */
268     gcmkFOOTER();
269     return status;
270 }
271
272 /*******************************************************************************
273 **
274 **  gckVIDMEM_DestroyVirtual
275 **
276 **  Destroy an gcuVIDMEM_NODE union for virtual memory.
277 **
278 **  INPUT:
279 **
280 **      gcuVIDMEM_NODE_PTR Node
281 **          Pointer to a gcuVIDMEM_NODE union.
282 **
283 **  OUTPUT:
284 **
285 **      Nothing.
286 */
287 gceSTATUS
288 gckVIDMEM_DestroyVirtual(
289     IN gcuVIDMEM_NODE_PTR Node
290     )
291 {
292     gckOS os;
293
294     gcmkHEADER_ARG("Node=0x%x", Node);
295
296     /* Verify the arguments. */
297     gcmkVERIFY_OBJECT(Node->Virtual.kernel, gcvOBJ_KERNEL);
298
299     /* Extact the gckOS object pointer. */
300     os = Node->Virtual.kernel->os;
301     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
302
303     /* Delete the gcuVIDMEM_NODE union. */
304     gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, Node));
305
306     /* Success. */
307     gcmkFOOTER_NO();
308     return gcvSTATUS_OK;
309 }
310
311 /*******************************************************************************
312 **
313 **  gckVIDMEM_Construct
314 **
315 **  Construct a new gckVIDMEM object.
316 **
317 **  INPUT:
318 **
319 **      gckOS Os
320 **          Pointer to an gckOS object.
321 **
322 **      gctUINT32 BaseAddress
323 **          Base address for the video memory heap.
324 **
325 **      gctSIZE_T Bytes
326 **          Number of bytes in the video memory heap.
327 **
328 **      gctSIZE_T Threshold
329 **          Minimum number of bytes beyond am allocation before the node is
330 **          split.  Can be used as a minimum alignment requirement.
331 **
332 **      gctSIZE_T BankSize
333 **          Number of bytes per physical memory bank.  Used by bank
334 **          optimization.
335 **
336 **  OUTPUT:
337 **
338 **      gckVIDMEM * Memory
339 **          Pointer to a variable that will hold the pointer to the gckVIDMEM
340 **          object.
341 */
342 gceSTATUS
343 gckVIDMEM_Construct(
344     IN gckOS Os,
345     IN gctUINT32 BaseAddress,
346     IN gctSIZE_T Bytes,
347     IN gctSIZE_T Threshold,
348     IN gctSIZE_T BankSize,
349     OUT gckVIDMEM * Memory
350     )
351 {
352     gckVIDMEM memory = gcvNULL;
353     gceSTATUS status;
354     gcuVIDMEM_NODE_PTR node;
355     gctINT i, banks = 0;
356     gctPOINTER pointer = gcvNULL;
357     gctUINT32 heapBytes;
358     gctUINT32 bankSize;
359
360     gcmkHEADER_ARG("Os=0x%x BaseAddress=%08x Bytes=%lu Threshold=%lu "
361                    "BankSize=%lu",
362                    Os, BaseAddress, Bytes, Threshold, BankSize);
363
364     /* Verify the arguments. */
365     gcmkVERIFY_OBJECT(Os, gcvOBJ_OS);
366     gcmkVERIFY_ARGUMENT(Bytes > 0);
367     gcmkVERIFY_ARGUMENT(Memory != gcvNULL);
368
369     gcmkSAFECASTSIZET(heapBytes, Bytes);
370     gcmkSAFECASTSIZET(bankSize, BankSize);
371
372     /* Allocate the gckVIDMEM object. */
373     gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(struct _gckVIDMEM), &pointer));
374
375     memory = pointer;
376
377     /* Initialize the gckVIDMEM object. */
378     memory->object.type = gcvOBJ_VIDMEM;
379     memory->os          = Os;
380
381     /* Set video memory heap information. */
382     memory->baseAddress = BaseAddress;
383     memory->bytes       = heapBytes;
384     memory->freeBytes   = heapBytes;
385     memory->threshold   = Threshold;
386     memory->mutex       = gcvNULL;
387
388     BaseAddress = 0;
389
390     /* Walk all possible banks. */
391     for (i = 0; i < gcmCOUNTOF(memory->sentinel); ++i)
392     {
393         gctUINT32 bytes;
394
395         if (BankSize == 0)
396         {
397             /* Use all bytes for the first bank. */
398             bytes = heapBytes;
399         }
400         else
401         {
402             /* Compute number of bytes for this bank. */
403             bytes = gcmALIGN(BaseAddress + 1, bankSize) - BaseAddress;
404
405             if (bytes > heapBytes)
406             {
407                 /* Make sure we don't exceed the total number of bytes. */
408                 bytes = heapBytes;
409             }
410         }
411
412         if (bytes == 0)
413         {
414             /* Mark heap is not used. */
415             memory->sentinel[i].VidMem.next     =
416             memory->sentinel[i].VidMem.prev     =
417             memory->sentinel[i].VidMem.nextFree =
418             memory->sentinel[i].VidMem.prevFree = gcvNULL;
419             continue;
420         }
421
422         /* Allocate one gcuVIDMEM_NODE union. */
423         gcmkONERROR(gckOS_Allocate(Os, gcmSIZEOF(gcuVIDMEM_NODE), &pointer));
424
425         node = pointer;
426
427         /* Initialize gcuVIDMEM_NODE union. */
428         node->VidMem.memory    = memory;
429
430         node->VidMem.next      =
431         node->VidMem.prev      =
432         node->VidMem.nextFree  =
433         node->VidMem.prevFree  = &memory->sentinel[i];
434
435         node->VidMem.offset    = BaseAddress;
436         node->VidMem.bytes     = bytes;
437         node->VidMem.alignment = 0;
438         node->VidMem.physical  = 0;
439         node->VidMem.pool      = gcvPOOL_UNKNOWN;
440
441         node->VidMem.locked    = 0;
442
443 #ifdef __QNXNTO__
444         node->VidMem.processID = 0;
445         node->VidMem.logical   = gcvNULL;
446 #endif
447
448 #if gcdENABLE_VG
449         node->VidMem.kernelVirtual = gcvNULL;
450 #endif
451
452         /* Initialize the linked list of nodes. */
453         memory->sentinel[i].VidMem.next     =
454         memory->sentinel[i].VidMem.prev     =
455         memory->sentinel[i].VidMem.nextFree =
456         memory->sentinel[i].VidMem.prevFree = node;
457
458         /* Mark sentinel. */
459         memory->sentinel[i].VidMem.bytes = 0;
460
461         /* Adjust address for next bank. */
462         BaseAddress += bytes;
463         heapBytes       -= bytes;
464         banks       ++;
465     }
466
467     /* Assign all the bank mappings. */
468     memory->mapping[gcvSURF_RENDER_TARGET]      = banks - 1;
469     memory->mapping[gcvSURF_BITMAP]             = banks - 1;
470     if (banks > 1) --banks;
471     memory->mapping[gcvSURF_DEPTH]              = banks - 1;
472     memory->mapping[gcvSURF_HIERARCHICAL_DEPTH] = banks - 1;
473     if (banks > 1) --banks;
474     memory->mapping[gcvSURF_TEXTURE]            = banks - 1;
475     if (banks > 1) --banks;
476     memory->mapping[gcvSURF_VERTEX]             = banks - 1;
477     if (banks > 1) --banks;
478     memory->mapping[gcvSURF_INDEX]              = banks - 1;
479     if (banks > 1) --banks;
480     memory->mapping[gcvSURF_TILE_STATUS]        = banks - 1;
481     if (banks > 1) --banks;
482     memory->mapping[gcvSURF_TYPE_UNKNOWN]       = 0;
483
484 #if gcdENABLE_VG
485     memory->mapping[gcvSURF_IMAGE]   = 0;
486     memory->mapping[gcvSURF_MASK]    = 0;
487     memory->mapping[gcvSURF_SCISSOR] = 0;
488 #endif
489
490     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
491                   "[GALCORE] INDEX:         bank %d",
492                   memory->mapping[gcvSURF_INDEX]);
493     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
494                   "[GALCORE] VERTEX:        bank %d",
495                   memory->mapping[gcvSURF_VERTEX]);
496     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
497                   "[GALCORE] TEXTURE:       bank %d",
498                   memory->mapping[gcvSURF_TEXTURE]);
499     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
500                   "[GALCORE] RENDER_TARGET: bank %d",
501                   memory->mapping[gcvSURF_RENDER_TARGET]);
502     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
503                   "[GALCORE] DEPTH:         bank %d",
504                   memory->mapping[gcvSURF_DEPTH]);
505     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
506                   "[GALCORE] TILE_STATUS:   bank %d",
507                   memory->mapping[gcvSURF_TILE_STATUS]);
508
509     /* Allocate the mutex. */
510     gcmkONERROR(gckOS_CreateMutex(Os, &memory->mutex));
511
512     /* Return pointer to the gckVIDMEM object. */
513     *Memory = memory;
514
515     /* Success. */
516     gcmkFOOTER_ARG("*Memory=0x%x", *Memory);
517     return gcvSTATUS_OK;
518
519 OnError:
520     /* Roll back. */
521     if (memory != gcvNULL)
522     {
523         if (memory->mutex != gcvNULL)
524         {
525             /* Delete the mutex. */
526             gcmkVERIFY_OK(gckOS_DeleteMutex(Os, memory->mutex));
527         }
528
529         for (i = 0; i < banks; ++i)
530         {
531             /* Free the heap. */
532             gcmkASSERT(memory->sentinel[i].VidMem.next != gcvNULL);
533             gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory->sentinel[i].VidMem.next));
534         }
535
536         /* Free the object. */
537         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Os, memory));
538     }
539
540     /* Return the status. */
541     gcmkFOOTER();
542     return status;
543 }
544
545 /*******************************************************************************
546 **
547 **  gckVIDMEM_Destroy
548 **
549 **  Destroy an gckVIDMEM object.
550 **
551 **  INPUT:
552 **
553 **      gckVIDMEM Memory
554 **          Pointer to an gckVIDMEM object to destroy.
555 **
556 **  OUTPUT:
557 **
558 **      Nothing.
559 */
560 gceSTATUS
561 gckVIDMEM_Destroy(
562     IN gckVIDMEM Memory
563     )
564 {
565     gcuVIDMEM_NODE_PTR node, next;
566     gctINT i;
567
568     gcmkHEADER_ARG("Memory=0x%x", Memory);
569
570     /* Verify the arguments. */
571     gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
572
573     /* Walk all sentinels. */
574     for (i = 0; i < gcmCOUNTOF(Memory->sentinel); ++i)
575     {
576         /* Bail out of the heap is not used. */
577         if (Memory->sentinel[i].VidMem.next == gcvNULL)
578         {
579             break;
580         }
581
582         /* Walk all the nodes until we reach the sentinel. */
583         for (node = Memory->sentinel[i].VidMem.next;
584              node->VidMem.bytes != 0;
585              node = next)
586         {
587             /* Save pointer to the next node. */
588             next = node->VidMem.next;
589
590             /* Free the node. */
591             gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, node));
592         }
593     }
594
595     /* Free the mutex. */
596     gcmkVERIFY_OK(gckOS_DeleteMutex(Memory->os, Memory->mutex));
597
598     /* Mark the object as unknown. */
599     Memory->object.type = gcvOBJ_UNKNOWN;
600
601     /* Free the gckVIDMEM object. */
602     gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Memory->os, Memory));
603
604     /* Success. */
605     gcmkFOOTER_NO();
606     return gcvSTATUS_OK;
607 }
608
609 #if gcdENABLE_BANK_ALIGNMENT
610
611 #if !gcdBANK_BIT_START
612 #error gcdBANK_BIT_START not defined.
613 #endif
614
615 #if !gcdBANK_BIT_END
616 #error gcdBANK_BIT_END not defined.
617 #endif
618 /*******************************************************************************
619 **  _GetSurfaceBankAlignment
620 **
621 **  Return the required offset alignment required to the make BaseAddress
622 **  aligned properly.
623 **
624 **  INPUT:
625 **
626 **      gckOS Os
627 **          Pointer to gcoOS object.
628 **
629 **      gceSURF_TYPE Type
630 **          Type of allocation.
631 **
632 **      gctUINT32 BaseAddress
633 **          Base address of current video memory node.
634 **
635 **  OUTPUT:
636 **
637 **      gctUINT32_PTR AlignmentOffset
638 **          Pointer to a variable that will hold the number of bytes to skip in
639 **          the current video memory node in order to make the alignment bank
640 **          aligned.
641 */
642 static gceSTATUS
643 _GetSurfaceBankAlignment(
644     IN gckKERNEL Kernel,
645     IN gceSURF_TYPE Type,
646     IN gctUINT32 BaseAddress,
647     OUT gctUINT32_PTR AlignmentOffset
648     )
649 {
650     gctUINT32 bank;
651     /* To retrieve the bank. */
652     static const gctUINT32 bankMask = (0xFFFFFFFF << gcdBANK_BIT_START)
653                                     ^ (0xFFFFFFFF << (gcdBANK_BIT_END + 1));
654
655     /* To retrieve the bank and all the lower bytes. */
656     static const gctUINT32 byteMask = ~(0xFFFFFFFF << (gcdBANK_BIT_END + 1));
657
658     gcmkHEADER_ARG("Type=%d BaseAddress=0x%x ", Type, BaseAddress);
659
660     /* Verify the arguments. */
661     gcmkVERIFY_ARGUMENT(AlignmentOffset != gcvNULL);
662
663     switch (Type)
664     {
665     case gcvSURF_RENDER_TARGET:
666         bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START);
667
668         /* Align to the first bank. */
669         *AlignmentOffset = (bank == 0) ?
670             0 :
671             ((1 << (gcdBANK_BIT_END + 1)) + 0) -  (BaseAddress & byteMask);
672         break;
673
674     case gcvSURF_DEPTH:
675         bank = (BaseAddress & bankMask) >> (gcdBANK_BIT_START);
676
677         /* Align to the third bank. */
678         *AlignmentOffset = (bank == 2) ?
679             0 :
680             ((1 << (gcdBANK_BIT_END + 1)) + (2 << gcdBANK_BIT_START)) -  (BaseAddress & byteMask);
681
682         /* Minimum 256 byte alignment needed for fast_msaa. */
683         if ((gcdBANK_CHANNEL_BIT > 7) ||
684             ((gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_FAST_MSAA) != gcvSTATUS_TRUE) &&
685              (gckHARDWARE_IsFeatureAvailable(Kernel->hardware, gcvFEATURE_SMALL_MSAA) != gcvSTATUS_TRUE)))
686         {
687             /* Add a channel offset at the channel bit. */
688             *AlignmentOffset += (1 << gcdBANK_CHANNEL_BIT);
689         }
690         break;
691
692     default:
693         /* no alignment needed. */
694         *AlignmentOffset = 0;
695     }
696
697     /* Return the status. */
698     gcmkFOOTER_ARG("*AlignmentOffset=%u", *AlignmentOffset);
699     return gcvSTATUS_OK;
700 }
701 #endif
702
703 static gcuVIDMEM_NODE_PTR
704 _FindNode(
705     IN gckKERNEL Kernel,
706     IN gckVIDMEM Memory,
707     IN gctINT Bank,
708     IN gctSIZE_T Bytes,
709     IN gceSURF_TYPE Type,
710     IN OUT gctUINT32_PTR Alignment
711     )
712 {
713     gcuVIDMEM_NODE_PTR node;
714     gctUINT32 alignment;
715
716 #if gcdENABLE_BANK_ALIGNMENT
717     gctUINT32 bankAlignment;
718     gceSTATUS status;
719 #endif
720
721     if (Memory->sentinel[Bank].VidMem.nextFree == gcvNULL)
722     {
723         /* No free nodes left. */
724         return gcvNULL;
725     }
726
727 #if gcdENABLE_BANK_ALIGNMENT
728     /* Walk all free nodes until we have one that is big enough or we have
729     ** reached the sentinel. */
730     for (node = Memory->sentinel[Bank].VidMem.nextFree;
731          node->VidMem.bytes != 0;
732          node = node->VidMem.nextFree)
733     {
734         if (node->VidMem.bytes < Bytes)
735         {
736             continue;
737         }
738
739         gcmkONERROR(_GetSurfaceBankAlignment(
740             Kernel,
741             Type,
742             node->VidMem.memory->baseAddress + node->VidMem.offset,
743             &bankAlignment));
744
745         bankAlignment = gcmALIGN(bankAlignment, *Alignment);
746
747         /* Compute number of bytes to skip for alignment. */
748         alignment = (*Alignment == 0)
749                   ? 0
750                   : (*Alignment - (node->VidMem.offset % *Alignment));
751
752         if (alignment == *Alignment)
753         {
754             /* Node is already aligned. */
755             alignment = 0;
756         }
757
758         if (node->VidMem.bytes >= Bytes + alignment + bankAlignment)
759         {
760             /* This node is big enough. */
761             *Alignment = alignment + bankAlignment;
762             return node;
763         }
764     }
765 #endif
766
767     /* Walk all free nodes until we have one that is big enough or we have
768        reached the sentinel. */
769     for (node = Memory->sentinel[Bank].VidMem.nextFree;
770          node->VidMem.bytes != 0;
771          node = node->VidMem.nextFree)
772     {
773         gctUINT offset;
774
775         gctINT modulo;
776
777         gcmkSAFECASTSIZET(offset, node->VidMem.offset);
778
779         modulo = gckMATH_ModuloInt(offset, *Alignment);
780
781         /* Compute number of bytes to skip for alignment. */
782         alignment = (*Alignment == 0) ? 0 : (*Alignment - modulo);
783
784         if (alignment == *Alignment)
785         {
786             /* Node is already aligned. */
787             alignment = 0;
788         }
789
790         if (node->VidMem.bytes >= Bytes + alignment)
791         {
792             /* This node is big enough. */
793             *Alignment = alignment;
794             return node;
795         }
796     }
797
798 #if gcdENABLE_BANK_ALIGNMENT
799 OnError:
800 #endif
801     /* Not enough memory. */
802     return gcvNULL;
803 }
804
805 /*******************************************************************************
806 **
807 **  gckVIDMEM_AllocateLinear
808 **
809 **  Allocate linear memory from the gckVIDMEM object.
810 **
811 **  INPUT:
812 **
813 **      gckVIDMEM Memory
814 **          Pointer to an gckVIDMEM object.
815 **
816 **      gctSIZE_T Bytes
817 **          Number of bytes to allocate.
818 **
819 **      gctUINT32 Alignment
820 **          Byte alignment for allocation.
821 **
822 **      gceSURF_TYPE Type
823 **          Type of surface to allocate (use by bank optimization).
824 **
825 **      gctBOOL Specified
826 **          If user must use this pool, it should set Specified to gcvTRUE,
827 **          otherwise allocator may reserve some memory for other usage, such
828 **          as small block size allocation request.
829 **
830 **  OUTPUT:
831 **
832 **      gcuVIDMEM_NODE_PTR * Node
833 **          Pointer to a variable that will hold the allocated memory node.
834 */
835 gceSTATUS
836 gckVIDMEM_AllocateLinear(
837     IN gckKERNEL Kernel,
838     IN gckVIDMEM Memory,
839     IN gctSIZE_T Bytes,
840     IN gctUINT32 Alignment,
841     IN gceSURF_TYPE Type,
842     IN gctBOOL Specified,
843     OUT gcuVIDMEM_NODE_PTR * Node
844     )
845 {
846     gceSTATUS status;
847     gcuVIDMEM_NODE_PTR node;
848     gctUINT32 alignment;
849     gctINT bank, i;
850     gctBOOL acquired = gcvFALSE;
851
852     gcmkHEADER_ARG("Memory=0x%x Bytes=%lu Alignment=%u Type=%d",
853                    Memory, Bytes, Alignment, Type);
854
855     /* Verify the arguments. */
856     gcmkVERIFY_OBJECT(Memory, gcvOBJ_VIDMEM);
857     gcmkVERIFY_ARGUMENT(Bytes > 0);
858     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
859     gcmkVERIFY_ARGUMENT(Type < gcvSURF_NUM_TYPES);
860
861     /* Acquire the mutex. */
862     gcmkONERROR(gckOS_AcquireMutex(Memory->os, Memory->mutex, gcvINFINITE));
863
864     acquired = gcvTRUE;
865
866     if (Bytes > Memory->freeBytes)
867     {
868         /* Not enough memory. */
869         status = gcvSTATUS_OUT_OF_MEMORY;
870         goto OnError;
871     }
872
873 #if gcdSMALL_BLOCK_SIZE
874     if ((Memory->freeBytes < (Memory->bytes/gcdRATIO_FOR_SMALL_MEMORY))
875     &&  (Bytes >= gcdSMALL_BLOCK_SIZE)
876     &&  (Specified == gcvFALSE)
877     )
878     {
879         /* The left memory is for small memory.*/
880         status = gcvSTATUS_OUT_OF_MEMORY;
881         goto OnError;
882     }
883 #endif
884
885     /* Find the default bank for this surface type. */
886     gcmkASSERT((gctINT) Type < gcmCOUNTOF(Memory->mapping));
887     bank      = Memory->mapping[Type];
888     alignment = Alignment;
889
890     /* Find a free node in the default bank. */
891     node = _FindNode(Kernel, Memory, bank, Bytes, Type, &alignment);
892
893     /* Out of memory? */
894     if (node == gcvNULL)
895     {
896         /* Walk all lower banks. */
897         for (i = bank - 1; i >= 0; --i)
898         {
899             /* Find a free node inside the current bank. */
900             node = _FindNode(Kernel, Memory, i, Bytes, Type, &alignment);
901             if (node != gcvNULL)
902             {
903                 break;
904             }
905         }
906     }
907
908     if (node == gcvNULL)
909     {
910         /* Walk all upper banks. */
911         for (i = bank + 1; i < gcmCOUNTOF(Memory->sentinel); ++i)
912         {
913             if (Memory->sentinel[i].VidMem.nextFree == gcvNULL)
914             {
915                 /* Abort when we reach unused banks. */
916                 break;
917             }
918
919             /* Find a free node inside the current bank. */
920             node = _FindNode(Kernel, Memory, i, Bytes, Type, &alignment);
921             if (node != gcvNULL)
922             {
923                 break;
924             }
925         }
926     }
927
928     if (node == gcvNULL)
929     {
930         /* Out of memory. */
931         status = gcvSTATUS_OUT_OF_MEMORY;
932         goto OnError;
933     }
934
935     /* Do we have an alignment? */
936     if (alignment > 0)
937     {
938         /* Split the node so it is aligned. */
939         if (_Split(Memory->os, node, alignment))
940         {
941             /* Successful split, move to aligned node. */
942             node = node->VidMem.next;
943
944             /* Remove alignment. */
945             alignment = 0;
946         }
947     }
948
949     /* Do we have enough memory after the allocation to split it? */
950     if (node->VidMem.bytes - Bytes > Memory->threshold)
951     {
952         /* Adjust the node size. */
953         _Split(Memory->os, node, Bytes);
954     }
955
956     /* Remove the node from the free list. */
957     node->VidMem.prevFree->VidMem.nextFree = node->VidMem.nextFree;
958     node->VidMem.nextFree->VidMem.prevFree = node->VidMem.prevFree;
959     node->VidMem.nextFree                  =
960     node->VidMem.prevFree                  = gcvNULL;
961
962     /* Fill in the information. */
963     node->VidMem.alignment = alignment;
964     node->VidMem.memory    = Memory;
965 #ifdef __QNXNTO__
966     node->VidMem.logical   = gcvNULL;
967     gcmkONERROR(gckOS_GetProcessID(&node->VidMem.processID));
968 #endif
969
970     /* Adjust the number of free bytes. */
971     Memory->freeBytes -= node->VidMem.bytes;
972
973 #if gcdENABLE_VG
974     node->VidMem.kernelVirtual = gcvNULL;
975 #endif
976
977     /* Release the mutex. */
978     gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
979
980     /* Return the pointer to the node. */
981     *Node = node;
982
983     gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
984                    "Allocated %u bytes @ 0x%x [0x%08X]",
985                    node->VidMem.bytes, node, node->VidMem.offset);
986
987     /* Success. */
988     gcmkFOOTER_ARG("*Node=0x%x", *Node);
989     return gcvSTATUS_OK;
990
991 OnError:
992     if (acquired)
993     {
994      /* Release the mutex. */
995         gcmkVERIFY_OK(gckOS_ReleaseMutex(Memory->os, Memory->mutex));
996     }
997
998     /* Return the status. */
999     gcmkFOOTER();
1000     return status;
1001 }
1002
1003 /*******************************************************************************
1004 **
1005 **  gckVIDMEM_Free
1006 **
1007 **  Free an allocated video memory node.
1008 **
1009 **  INPUT:
1010 **
1011 **      gckKERNEL Kernel
1012 **          Pointer to an gckKERNEL object.
1013 **
1014 **      gcuVIDMEM_NODE_PTR Node
1015 **          Pointer to a gcuVIDMEM_NODE object.
1016 **
1017 **  OUTPUT:
1018 **
1019 **      Nothing.
1020 */
1021 gceSTATUS
1022 gckVIDMEM_Free(
1023     IN gckKERNEL Kernel,
1024     IN gcuVIDMEM_NODE_PTR Node
1025     )
1026 {
1027     gceSTATUS status;
1028     gckKERNEL kernel = gcvNULL;
1029     gckVIDMEM memory = gcvNULL;
1030     gcuVIDMEM_NODE_PTR node;
1031     gctBOOL mutexAcquired = gcvFALSE;
1032
1033     gcmkHEADER_ARG("Node=0x%x", Node);
1034
1035     /* Verify the arguments. */
1036     if ((Node == gcvNULL)
1037     ||  (Node->VidMem.memory == gcvNULL)
1038     )
1039     {
1040         /* Invalid object. */
1041         gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
1042     }
1043
1044     /**************************** Video Memory ********************************/
1045
1046     if (Node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1047     {
1048         /* Extract pointer to gckVIDMEM object owning the node. */
1049         memory = Node->VidMem.memory;
1050
1051         /* Acquire the mutex. */
1052         gcmkONERROR(
1053             gckOS_AcquireMutex(memory->os, memory->mutex, gcvINFINITE));
1054
1055         mutexAcquired = gcvTRUE;
1056
1057 #ifdef __QNXNTO__
1058         /* Unmap the video memory. */
1059         if (Node->VidMem.logical != gcvNULL)
1060         {
1061             gckKERNEL_UnmapVideoMemory(
1062                     Kernel,
1063                     Node->VidMem.logical,
1064                     Node->VidMem.processID,
1065                     Node->VidMem.bytes);
1066             Node->VidMem.logical = gcvNULL;
1067         }
1068
1069         /* Reset. */
1070         Node->VidMem.processID = 0;
1071
1072         /* Don't try to re-free an already freed node. */
1073         if ((Node->VidMem.nextFree == gcvNULL)
1074         &&  (Node->VidMem.prevFree == gcvNULL)
1075         )
1076 #endif
1077         {
1078 #if gcdENABLE_VG
1079             if (Node->VidMem.kernelVirtual)
1080             {
1081                 gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1082                         "%s(%d) Unmap %x from kernel space.",
1083                         __FUNCTION__, __LINE__,
1084                         Node->VidMem.kernelVirtual);
1085
1086                 gcmkVERIFY_OK(
1087                     gckOS_UnmapPhysical(memory->os,
1088                                         Node->VidMem.kernelVirtual,
1089                                         Node->VidMem.bytes));
1090
1091                 Node->VidMem.kernelVirtual = gcvNULL;
1092             }
1093 #endif
1094
1095             /* Check if Node is already freed. */
1096             if (Node->VidMem.nextFree)
1097             {
1098                 /* Node is alread freed. */
1099                 gcmkONERROR(gcvSTATUS_INVALID_DATA);
1100             }
1101
1102             /* Update the number of free bytes. */
1103             memory->freeBytes += Node->VidMem.bytes;
1104
1105             /* Find the next free node. */
1106             for (node = Node->VidMem.next;
1107                  node != gcvNULL && node->VidMem.nextFree == gcvNULL;
1108                  node = node->VidMem.next) ;
1109
1110             /* Insert this node in the free list. */
1111             Node->VidMem.nextFree = node;
1112             Node->VidMem.prevFree = node->VidMem.prevFree;
1113
1114             Node->VidMem.prevFree->VidMem.nextFree =
1115             node->VidMem.prevFree                  = Node;
1116
1117             /* Is the next node a free node and not the sentinel? */
1118             if ((Node->VidMem.next == Node->VidMem.nextFree)
1119             &&  (Node->VidMem.next->VidMem.bytes != 0)
1120             )
1121             {
1122                 /* Merge this node with the next node. */
1123                 gcmkONERROR(_Merge(memory->os, node = Node));
1124                 gcmkASSERT(node->VidMem.nextFree != node);
1125                 gcmkASSERT(node->VidMem.prevFree != node);
1126             }
1127
1128             /* Is the previous node a free node and not the sentinel? */
1129             if ((Node->VidMem.prev == Node->VidMem.prevFree)
1130             &&  (Node->VidMem.prev->VidMem.bytes != 0)
1131             )
1132             {
1133                 /* Merge this node with the previous node. */
1134                 gcmkONERROR(_Merge(memory->os, node = Node->VidMem.prev));
1135                 gcmkASSERT(node->VidMem.nextFree != node);
1136                 gcmkASSERT(node->VidMem.prevFree != node);
1137             }
1138         }
1139
1140         /* Release the mutex. */
1141         gcmkVERIFY_OK(gckOS_ReleaseMutex(memory->os, memory->mutex));
1142
1143         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1144                        "Node 0x%x is freed.",
1145                        Node);
1146
1147         /* Success. */
1148         gcmkFOOTER_NO();
1149         return gcvSTATUS_OK;
1150     }
1151
1152     /*************************** Virtual Memory *******************************/
1153
1154     /* Get gckKERNEL object. */
1155     kernel = Node->Virtual.kernel;
1156
1157     /* Verify the gckKERNEL object pointer. */
1158     gcmkVERIFY_OBJECT(kernel, gcvOBJ_KERNEL);
1159
1160 #if gcdENABLE_VG
1161     if (Node->Virtual.kernelVirtual)
1162     {
1163         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1164                 "%s(%d) Unmap %x from kernel space.",
1165                 __FUNCTION__, __LINE__,
1166                 Node->Virtual.kernelVirtual);
1167
1168         gcmkVERIFY_OK(
1169             gckOS_UnmapPhysical(kernel->os,
1170                                 Node->Virtual.kernelVirtual,
1171                                 Node->Virtual.bytes));
1172
1173         Node->Virtual.kernelVirtual = gcvNULL;
1174     }
1175 #endif
1176
1177     /* Free the virtual memory. */
1178     gcmkVERIFY_OK(gckOS_FreePagedMemory(kernel->os,
1179                                         Node->Virtual.physical,
1180                                         Node->Virtual.bytes));
1181
1182     /* Destroy the gcuVIDMEM_NODE union. */
1183     gcmkVERIFY_OK(gckVIDMEM_DestroyVirtual(Node));
1184
1185     /* Success. */
1186     gcmkFOOTER_NO();
1187     return gcvSTATUS_OK;
1188
1189 OnError:
1190     if (mutexAcquired)
1191     {
1192         /* Release the mutex. */
1193         gcmkVERIFY_OK(gckOS_ReleaseMutex(
1194             memory->os, memory->mutex
1195             ));
1196     }
1197
1198     /* Return the status. */
1199     gcmkFOOTER();
1200     return status;
1201 }
1202
1203 #if !gcdPROCESS_ADDRESS_SPACE
1204 /*******************************************************************************
1205 **
1206 ** _NeedVirtualMapping
1207 **
1208 **  Whether setup GPU page table for video node.
1209 **
1210 **  INPUT:
1211 **      gckKERNEL Kernel
1212 **          Pointer to an gckKERNEL object.
1213 **
1214 **      gcuVIDMEM_NODE_PTR Node
1215 **          Pointer to a gcuVIDMEM_NODE union.
1216 **
1217 **      gceCORE  Core
1218 **          Id of current GPU.
1219 **
1220 **  OUTPUT:
1221 **      gctBOOL * NeedMapping
1222 **          A pointer hold the result whether Node should be mapping.
1223 */
1224 static gceSTATUS
1225 _NeedVirtualMapping(
1226     IN gckKERNEL Kernel,
1227     IN gceCORE  Core,
1228     IN gcuVIDMEM_NODE_PTR Node,
1229     OUT gctBOOL * NeedMapping
1230 )
1231 {
1232     gceSTATUS status;
1233     gctUINT32 phys;
1234     gctUINT32 end;
1235     gcePOOL pool;
1236     gctUINT32 offset;
1237     gctUINT32 baseAddress;
1238     gctUINT32 bytes;
1239
1240     gcmkHEADER_ARG("Node=0x%X", Node);
1241
1242     /* Verify the arguments. */
1243     gcmkVERIFY_ARGUMENT(Kernel != gcvNULL);
1244     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
1245     gcmkVERIFY_ARGUMENT(NeedMapping != gcvNULL);
1246     gcmkVERIFY_ARGUMENT(Core < gcdMAX_GPU_COUNT);
1247
1248     if (Node->Virtual.contiguous)
1249     {
1250 #if gcdENABLE_VG
1251         if (Core == gcvCORE_VG)
1252         {
1253             *NeedMapping = gcvFALSE;
1254         }
1255         else
1256 #endif
1257         {
1258             /* Convert logical address into a physical address. */
1259             gcmkONERROR(gckOS_UserLogicalToPhysical(
1260                         Kernel->os, Node->Virtual.logical, &phys
1261                         ));
1262
1263             gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &baseAddress));
1264
1265             gcmkASSERT(phys >= baseAddress);
1266
1267             /* Subtract baseAddress to get a GPU address used for programming. */
1268             phys -= baseAddress;
1269
1270             /* If part of region is belong to gcvPOOL_VIRTUAL,
1271             ** whole region has to be mapped. */
1272             gcmkSAFECASTSIZET(bytes, Node->Virtual.bytes);
1273             end = phys + bytes - 1;
1274
1275             gcmkONERROR(gckHARDWARE_SplitMemory(
1276                         Kernel->hardware, end, &pool, &offset
1277                         ));
1278
1279             *NeedMapping = (pool == gcvPOOL_VIRTUAL);
1280         }
1281     }
1282     else
1283     {
1284         *NeedMapping = gcvTRUE;
1285     }
1286
1287     gcmkFOOTER_ARG("*NeedMapping=%d", *NeedMapping);
1288     return gcvSTATUS_OK;
1289
1290 OnError:
1291     gcmkFOOTER();
1292     return status;
1293 }
1294 #endif
1295
1296 #if gcdPROCESS_ADDRESS_SPACE
1297 gcsGPU_MAP_PTR
1298 _FindGPUMap(
1299     IN gcsGPU_MAP_PTR Head,
1300     IN gctINT ProcessID
1301     )
1302 {
1303     gcsGPU_MAP_PTR map = Head;
1304
1305     while (map)
1306     {
1307         if (map->pid == ProcessID)
1308         {
1309             return map;
1310         }
1311
1312         map = map->next;
1313     }
1314
1315     return gcvNULL;
1316 }
1317
1318 gcsGPU_MAP_PTR
1319 _CreateGPUMap(
1320     IN gckOS Os,
1321     IN gcsGPU_MAP_PTR *Head,
1322     IN gcsGPU_MAP_PTR *Tail,
1323     IN gctINT ProcessID
1324     )
1325 {
1326     gcsGPU_MAP_PTR gpuMap;
1327     gctPOINTER pointer = gcvNULL;
1328
1329     gckOS_Allocate(Os, sizeof(gcsGPU_MAP), &pointer);
1330
1331     if (pointer == gcvNULL)
1332     {
1333         return gcvNULL;
1334     }
1335
1336     gpuMap = pointer;
1337
1338     gckOS_ZeroMemory(pointer, sizeof(gcsGPU_MAP));
1339
1340     gpuMap->pid = ProcessID;
1341
1342     if (!*Head)
1343     {
1344         *Head = *Tail = gpuMap;
1345     }
1346     else
1347     {
1348         gpuMap->prev = *Tail;
1349         (*Tail)->next = gpuMap;
1350         *Tail = gpuMap;
1351     }
1352
1353     return gpuMap;
1354 }
1355
1356 void
1357 _DestroyGPUMap(
1358     IN gckOS Os,
1359     IN gcsGPU_MAP_PTR *Head,
1360     IN gcsGPU_MAP_PTR *Tail,
1361     IN gcsGPU_MAP_PTR gpuMap
1362     )
1363 {
1364
1365     if (gpuMap == *Head)
1366     {
1367         if ((*Head = gpuMap->next) == gcvNULL)
1368         {
1369             *Tail = gcvNULL;
1370         }
1371     }
1372     else
1373     {
1374         gpuMap->prev->next = gpuMap->next;
1375         if (gpuMap == *Tail)
1376         {
1377             *Tail = gpuMap->prev;
1378         }
1379         else
1380         {
1381             gpuMap->next->prev = gpuMap->prev;
1382         }
1383     }
1384
1385     gcmkOS_SAFE_FREE(Os, gpuMap);
1386 }
1387 #endif
1388
1389 /*******************************************************************************
1390 **
1391 **  gckVIDMEM_Lock
1392 **
1393 **  Lock a video memory node and return its hardware specific address.
1394 **
1395 **  INPUT:
1396 **
1397 **      gckKERNEL Kernel
1398 **          Pointer to an gckKERNEL object.
1399 **
1400 **      gcuVIDMEM_NODE_PTR Node
1401 **          Pointer to a gcuVIDMEM_NODE union.
1402 **
1403 **  OUTPUT:
1404 **
1405 **      gctUINT32 * Address
1406 **          Pointer to a variable that will hold the hardware specific address.
1407 **
1408 **      gctUINT32 * PhysicalAddress
1409 **          Pointer to a variable that will hold the bus address of a contiguous
1410 **          video node.
1411 */
1412 gceSTATUS
1413 gckVIDMEM_Lock(
1414     IN gckKERNEL Kernel,
1415     IN gckVIDMEM_NODE Node,
1416     IN gctBOOL Cacheable,
1417     OUT gctUINT32 * Address,
1418     OUT gctUINT32 * Gid,
1419     OUT gctUINT64 * PhysicalAddress
1420     )
1421 {
1422     gceSTATUS status;
1423     gctBOOL acquired = gcvFALSE;
1424     gctBOOL locked = gcvFALSE;
1425     gckOS os = gcvNULL;
1426 #if !gcdPROCESS_ADDRESS_SPACE
1427     gctBOOL needMapping = gcvFALSE;
1428 #endif
1429     gctUINT32 baseAddress;
1430     gctUINT32 physicalAddress;
1431     gcuVIDMEM_NODE_PTR node = Node->node;
1432
1433     gcmkHEADER_ARG("Node=0x%x", Node);
1434
1435     /* Verify the arguments. */
1436     gcmkVERIFY_ARGUMENT(Address != gcvNULL);
1437     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
1438
1439     /* Extract the gckOS object pointer. */
1440     os = Kernel->os;
1441     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
1442
1443     if ((node == gcvNULL)
1444     ||  (node->VidMem.memory == gcvNULL)
1445     )
1446     {
1447         /* Invalid object. */
1448         gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
1449     }
1450
1451     /* Grab the mutex. */
1452     gcmkONERROR(gckOS_AcquireMutex(os, Node->mutex, gcvINFINITE));
1453     acquired = gcvTRUE;
1454
1455     /**************************** Video Memory ********************************/
1456
1457     if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1458     {
1459         gctUINT32 offset;
1460
1461         if (Cacheable == gcvTRUE)
1462         {
1463             gcmkONERROR(gcvSTATUS_INVALID_REQUEST);
1464         }
1465
1466         /* Increment the lock count. */
1467         node->VidMem.locked ++;
1468
1469         /* Return the physical address of the node. */
1470         gcmkSAFECASTSIZET(offset, node->VidMem.offset);
1471
1472         *Address = node->VidMem.memory->baseAddress
1473                  + offset
1474                  + node->VidMem.alignment;
1475
1476         physicalAddress = *Address;
1477
1478         /* Get hardware specific address. */
1479 #if gcdENABLE_VG
1480         if (Kernel->vg == gcvNULL)
1481 #endif
1482         {
1483             if (Kernel->hardware->mmuVersion == 0)
1484             {
1485                 /* Convert physical to GPU address for old mmu. */
1486                 gcmkONERROR(gckOS_GetBaseAddress(Kernel->os, &baseAddress));
1487                 gcmkASSERT(*Address > baseAddress);
1488                 *Address -= baseAddress;
1489             }
1490         }
1491
1492         gcmkVERIFY_OK(gckOS_CPUPhysicalToGPUPhysical(
1493             Kernel->os,
1494             *Address,
1495             Address
1496             ));
1497
1498         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1499                       "Locked node 0x%x (%d) @ 0x%08X",
1500                       node,
1501                       node->VidMem.locked,
1502                       *Address);
1503     }
1504
1505     /*************************** Virtual Memory *******************************/
1506
1507     else
1508     {
1509
1510         *Gid = node->Virtual.gid;
1511
1512 #if gcdPAGED_MEMORY_CACHEABLE
1513         /* Force video memory cacheable. */
1514         Cacheable = gcvTRUE;
1515 #endif
1516
1517         gcmkONERROR(
1518             gckOS_LockPages(os,
1519                             node->Virtual.physical,
1520                             node->Virtual.bytes,
1521                             Cacheable,
1522                             &node->Virtual.logical,
1523                             &node->Virtual.pageCount));
1524
1525         gcmkONERROR(gckOS_GetPhysicalAddress(
1526             os,
1527             node->Virtual.logical,
1528             &physicalAddress
1529             ));
1530
1531 #if gcdENABLE_VG
1532         node->Virtual.physicalAddress = physicalAddress;
1533 #endif
1534
1535 #if !gcdPROCESS_ADDRESS_SPACE
1536         /* Increment the lock count. */
1537         if (node->Virtual.lockeds[Kernel->core] ++ == 0)
1538         {
1539             locked = gcvTRUE;
1540
1541             gcmkONERROR(_NeedVirtualMapping(Kernel, Kernel->core, node, &needMapping));
1542
1543             if (needMapping == gcvFALSE)
1544             {
1545                 /* Get hardware specific address. */
1546 #if gcdENABLE_VG
1547                 if (Kernel->vg != gcvNULL)
1548                 {
1549                     gcmkONERROR(gckVGHARDWARE_ConvertLogical(
1550                                 Kernel->vg->hardware,
1551                                 node->Virtual.logical,
1552                                 gcvTRUE,
1553                                 &node->Virtual.addresses[Kernel->core]));
1554                 }
1555                 else
1556 #endif
1557                 {
1558                     gcmkONERROR(gckHARDWARE_ConvertLogical(
1559                                 Kernel->hardware,
1560                                 node->Virtual.logical,
1561                                 gcvTRUE,
1562                                 &node->Virtual.addresses[Kernel->core]));
1563                 }
1564             }
1565             else
1566             {
1567 #if gcdSECURITY
1568                 gctPHYS_ADDR physicalArrayPhysical;
1569                 gctPOINTER physicalArrayLogical;
1570
1571                 gcmkONERROR(gckOS_AllocatePageArray(
1572                     os,
1573                     node->Virtual.physical,
1574                     node->Virtual.pageCount,
1575                     &physicalArrayLogical,
1576                     &physicalArrayPhysical
1577                     ));
1578
1579                 gcmkONERROR(gckKERNEL_SecurityMapMemory(
1580                     Kernel,
1581                     physicalArrayLogical,
1582                     node->Virtual.pageCount,
1583                     &node->Virtual.addresses[Kernel->core]
1584                     ));
1585
1586                 gcmkONERROR(gckOS_FreeNonPagedMemory(
1587                     os,
1588                     1,
1589                     physicalArrayPhysical,
1590                     physicalArrayLogical
1591                     ));
1592 #else
1593 #if gcdENABLE_VG
1594                 if (Kernel->vg != gcvNULL)
1595                 {
1596                     /* Allocate pages inside the MMU. */
1597                     gcmkONERROR(
1598                         gckVGMMU_AllocatePages(Kernel->vg->mmu,
1599                                              node->Virtual.pageCount,
1600                                              &node->Virtual.pageTables[Kernel->core],
1601                                              &node->Virtual.addresses[Kernel->core]));
1602                 }
1603                 else
1604 #endif
1605                 {
1606                     /* Allocate pages inside the MMU. */
1607                     gcmkONERROR(
1608                         gckMMU_AllocatePagesEx(Kernel->mmu,
1609                                              node->Virtual.pageCount,
1610                                              node->Virtual.type,
1611                                              &node->Virtual.pageTables[Kernel->core],
1612                                              &node->Virtual.addresses[Kernel->core]));
1613                 }
1614
1615                 node->Virtual.lockKernels[Kernel->core] = Kernel;
1616
1617                 /* Map the pages. */
1618                 gcmkONERROR(
1619                     gckOS_MapPagesEx(os,
1620                                      Kernel->core,
1621                                      node->Virtual.physical,
1622                                      node->Virtual.pageCount,
1623                                      node->Virtual.addresses[Kernel->core],
1624                                      node->Virtual.pageTables[Kernel->core]));
1625
1626 #if gcdENABLE_VG
1627                 if (Kernel->core == gcvCORE_VG)
1628                 {
1629                     gcmkONERROR(gckVGMMU_Flush(Kernel->vg->mmu));
1630                 }
1631                 else
1632 #endif
1633                 {
1634                     gcmkONERROR(gckMMU_Flush(Kernel->mmu, node->Virtual.type));
1635                 }
1636 #endif
1637             }
1638             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1639                            "Mapped virtual node 0x%x to 0x%08X",
1640                            node,
1641                            node->Virtual.addresses[Kernel->core]);
1642         }
1643
1644         /* Return hardware address. */
1645         *Address = node->Virtual.addresses[Kernel->core];
1646 #endif
1647     }
1648
1649     /* Release the mutex. */
1650     gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex));
1651
1652     *PhysicalAddress = (gctUINT64)physicalAddress;
1653
1654     /* Success. */
1655     gcmkFOOTER_ARG("*Address=%08x", *Address);
1656     return gcvSTATUS_OK;
1657
1658 OnError:
1659     if (locked)
1660     {
1661         if (node->Virtual.pageTables[Kernel->core] != gcvNULL)
1662         {
1663 #if gcdENABLE_VG
1664             if (Kernel->vg != gcvNULL)
1665             {
1666                 /* Free the pages from the MMU. */
1667                 gcmkVERIFY_OK(
1668                     gckVGMMU_FreePages(Kernel->vg->mmu,
1669                                      node->Virtual.pageTables[Kernel->core],
1670                                      node->Virtual.pageCount));
1671             }
1672             else
1673 #endif
1674             {
1675                 /* Free the pages from the MMU. */
1676                 gcmkVERIFY_OK(
1677                     gckMMU_FreePages(Kernel->mmu,
1678                                      node->Virtual.pageTables[Kernel->core],
1679                                      node->Virtual.pageCount));
1680             }
1681             node->Virtual.pageTables[Kernel->core]  = gcvNULL;
1682             node->Virtual.lockKernels[Kernel->core] = gcvNULL;
1683         }
1684
1685         /* Unlock the pages. */
1686         gcmkVERIFY_OK(
1687             gckOS_UnlockPages(os,
1688                               node->Virtual.physical,
1689                               node->Virtual.bytes,
1690                               node->Virtual.logical
1691                               ));
1692
1693         node->Virtual.lockeds[Kernel->core]--;
1694     }
1695
1696     if (acquired)
1697     {
1698         /* Release the mutex. */
1699         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex));
1700     }
1701
1702     /* Return the status. */
1703     gcmkFOOTER();
1704     return status;
1705 }
1706
1707 /*******************************************************************************
1708 **
1709 **  gckVIDMEM_Unlock
1710 **
1711 **  Unlock a video memory node.
1712 **
1713 **  INPUT:
1714 **
1715 **      gckKERNEL Kernel
1716 **          Pointer to an gckKERNEL object.
1717 **
1718 **      gcuVIDMEM_NODE_PTR Node
1719 **          Pointer to a locked gcuVIDMEM_NODE union.
1720 **
1721 **      gceSURF_TYPE Type
1722 **          Type of surface to unlock.
1723 **
1724 **      gctBOOL * Asynchroneous
1725 **          Pointer to a variable specifying whether the surface should be
1726 **          unlocked asynchroneously or not.
1727 **
1728 **  OUTPUT:
1729 **
1730 **      gctBOOL * Asynchroneous
1731 **          Pointer to a variable receiving the number of bytes used in the
1732 **          command buffer specified by 'Commands'.  If gcvNULL, there is no
1733 **          command buffer.
1734 */
1735 gceSTATUS
1736 gckVIDMEM_Unlock(
1737     IN gckKERNEL Kernel,
1738     IN gckVIDMEM_NODE Node,
1739     IN gceSURF_TYPE Type,
1740     IN OUT gctBOOL * Asynchroneous
1741     )
1742 {
1743     gceSTATUS status;
1744     gckOS os = gcvNULL;
1745     gctBOOL acquired = gcvFALSE;
1746     gcuVIDMEM_NODE_PTR node = Node->node;
1747
1748     gcmkHEADER_ARG("Node=0x%x Type=%d *Asynchroneous=%d",
1749                    Node, Type, gcmOPT_VALUE(Asynchroneous));
1750
1751     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
1752
1753     /* Get the gckOS object pointer. */
1754     os = Kernel->os;
1755     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
1756
1757     /* Verify the arguments. */
1758     if ((node == gcvNULL)
1759     ||  (node->VidMem.memory == gcvNULL)
1760     )
1761     {
1762         /* Invalid object. */
1763         gcmkONERROR(gcvSTATUS_INVALID_OBJECT);
1764     }
1765
1766     /* Grab the mutex. */
1767     gcmkONERROR(gckOS_AcquireMutex(os, Node->mutex, gcvINFINITE));
1768     acquired = gcvTRUE;
1769
1770     /**************************** Video Memory ********************************/
1771
1772     if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1773     {
1774         if (node->VidMem.locked <= 0)
1775         {
1776             /* The surface was not locked. */
1777             status = gcvSTATUS_MEMORY_UNLOCKED;
1778             goto OnError;
1779         }
1780
1781         if (Asynchroneous != gcvNULL)
1782         {
1783             /* Schedule an event to sync with GPU. */
1784             *Asynchroneous = gcvTRUE;
1785         }
1786         else
1787         {
1788             /* Decrement the lock count. */
1789             node->VidMem.locked --;
1790         }
1791
1792         gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1793                       "Unlocked node 0x%x (%d)",
1794                       node,
1795                       node->VidMem.locked);
1796     }
1797
1798     /*************************** Virtual Memory *******************************/
1799
1800     else
1801     {
1802
1803
1804         if (Asynchroneous == gcvNULL)
1805         {
1806 #if !gcdPROCESS_ADDRESS_SPACE
1807             if (node->Virtual.lockeds[Kernel->core] == 0)
1808             {
1809                 status = gcvSTATUS_MEMORY_UNLOCKED;
1810                 goto OnError;
1811             }
1812
1813             /* Decrement lock count. */
1814             -- node->Virtual.lockeds[Kernel->core];
1815
1816             /* See if we can unlock the resources. */
1817             if (node->Virtual.lockeds[Kernel->core] == 0)
1818             {
1819 #if gcdSECURITY
1820                 if (node->Virtual.addresses[Kernel->core] > 0x80000000)
1821                 {
1822                     gcmkONERROR(gckKERNEL_SecurityUnmapMemory(
1823                         Kernel,
1824                         node->Virtual.addresses[Kernel->core],
1825                         node->Virtual.pageCount
1826                         ));
1827                 }
1828 #else
1829                 /* Free the page table. */
1830                 if (node->Virtual.pageTables[Kernel->core] != gcvNULL)
1831                 {
1832 #if gcdENABLE_VG
1833                     if (Kernel->vg != gcvNULL)
1834                     {
1835                         gcmkONERROR(
1836                             gckVGMMU_FreePages(Kernel->vg->mmu,
1837                                              node->Virtual.pageTables[Kernel->core],
1838                                              node->Virtual.pageCount));
1839                     }
1840                     else
1841 #endif
1842                     {
1843                         gcmkONERROR(
1844                             gckMMU_FreePages(Kernel->mmu,
1845                                              node->Virtual.pageTables[Kernel->core],
1846                                              node->Virtual.pageCount));
1847                     }
1848
1849                     gcmkONERROR(gckOS_UnmapPages(
1850                         Kernel->os,
1851                         node->Virtual.pageCount,
1852                         node->Virtual.addresses[Kernel->core]
1853                         ));
1854
1855                     /* Mark page table as freed. */
1856                     node->Virtual.pageTables[Kernel->core] = gcvNULL;
1857                     node->Virtual.lockKernels[Kernel->core] = gcvNULL;
1858                 }
1859 #endif
1860             }
1861
1862             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1863                            "Unmapped virtual node 0x%x from 0x%08X",
1864                            node, node->Virtual.addresses[Kernel->core]);
1865 #endif
1866
1867         }
1868
1869         else
1870         {
1871             gcmkONERROR(
1872                 gckOS_UnlockPages(os,
1873                               node->Virtual.physical,
1874                               node->Virtual.bytes,
1875                               node->Virtual.logical));
1876
1877             gcmkTRACE_ZONE(gcvLEVEL_INFO, gcvZONE_VIDMEM,
1878                            "Scheduled unlock for virtual node 0x%x",
1879                            node);
1880
1881             /* Schedule the surface to be unlocked. */
1882             *Asynchroneous = gcvTRUE;
1883         }
1884     }
1885
1886     /* Release the mutex. */
1887     gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex));
1888     acquired = gcvFALSE;
1889
1890     /* Success. */
1891     gcmkFOOTER_ARG("*Asynchroneous=%d", gcmOPT_VALUE(Asynchroneous));
1892     return gcvSTATUS_OK;
1893
1894 OnError:
1895     if (acquired)
1896     {
1897         /* Release the mutex. */
1898         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mutex));
1899     }
1900
1901     /* Return the status. */
1902     gcmkFOOTER();
1903     return status;
1904 }
1905
1906 #if gcdPROCESS_ADDRESS_SPACE
1907 gceSTATUS
1908 gckVIDMEM_Node_Lock(
1909     IN gckKERNEL Kernel,
1910     IN gckVIDMEM_NODE Node,
1911     OUT gctUINT32 *Address
1912     )
1913 {
1914     gceSTATUS           status;
1915     gckOS               os;
1916     gcuVIDMEM_NODE_PTR  node = Node->node;
1917     gcsGPU_MAP_PTR      gpuMap;
1918     gctPHYS_ADDR        physical = gcvNULL;
1919     gctUINT32           phys = gcvINVALID_ADDRESS;
1920     gctUINT32           processID;
1921     gcsLOCK_INFO_PTR    lockInfo;
1922     gctUINT32           pageCount;
1923     gckMMU              mmu;
1924     gctUINT32           i;
1925     gctUINT32_PTR       pageTableEntry;
1926     gctUINT32           offset = 0;
1927     gctBOOL             acquired = gcvFALSE;
1928
1929     gcmkHEADER_ARG("Node = %x", Node);
1930
1931     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
1932     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
1933     gcmkVERIFY_ARGUMENT(Address != gcvNULL);
1934
1935     os = Kernel->os;
1936     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
1937
1938     gcmkONERROR(gckOS_GetProcessID(&processID));
1939
1940     gcmkONERROR(gckKERNEL_GetProcessMMU(Kernel, &mmu));
1941
1942     gcmkONERROR(gckOS_AcquireMutex(os, Node->mapMutex, gcvINFINITE));
1943     acquired = gcvTRUE;
1944
1945     /* Get map information for current process. */
1946     gpuMap = _FindGPUMap(Node->mapHead, processID);
1947
1948     if (gpuMap == gcvNULL)
1949     {
1950         gpuMap = _CreateGPUMap(os, &Node->mapHead, &Node->mapTail, processID);
1951
1952         if (gpuMap == gcvNULL)
1953         {
1954             gcmkONERROR(gcvSTATUS_OUT_OF_MEMORY);
1955         }
1956     }
1957
1958     lockInfo = &gpuMap->lockInfo;
1959
1960     if (lockInfo->lockeds[Kernel->core] ++ == 0)
1961     {
1962         /* Get necessary information. */
1963         if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
1964         {
1965             phys = node->VidMem.memory->baseAddress
1966                  + node->VidMem.offset
1967                  + node->VidMem.alignment;
1968
1969             /* GPU page table use 4K page. */
1970             pageCount = ((phys + node->VidMem.bytes + 4096 - 1) >> 12)
1971                       - (phys >> 12);
1972
1973             offset = phys & 0xFFF;
1974         }
1975         else
1976         {
1977             pageCount = node->Virtual.pageCount;
1978             physical = node->Virtual.physical;
1979         }
1980
1981         /* Allocate pages inside the MMU. */
1982         gcmkONERROR(gckMMU_AllocatePages(
1983             mmu,
1984             pageCount,
1985             &lockInfo->pageTables[Kernel->core],
1986             &lockInfo->GPUAddresses[Kernel->core]));
1987
1988         /* Record MMU from which pages are allocated.  */
1989         lockInfo->lockMmus[Kernel->core] = mmu;
1990
1991         pageTableEntry = lockInfo->pageTables[Kernel->core];
1992
1993         /* Fill page table entries. */
1994         if (phys != gcvINVALID_ADDRESS)
1995         {
1996             gctUINT32 address = lockInfo->GPUAddresses[Kernel->core];
1997             for (i = 0; i < pageCount; i++)
1998             {
1999                 gckMMU_GetPageEntry(mmu, address, &pageTableEntry);
2000                 gckMMU_SetPage(mmu, phys & 0xFFFFF000, pageTableEntry);
2001                 phys += 4096;
2002                 address += 4096;
2003                 pageTableEntry += 1;
2004             }
2005         }
2006         else
2007         {
2008             gctUINT32 address = lockInfo->GPUAddresses[Kernel->core];
2009             gcmkASSERT(physical != gcvNULL);
2010             gcmkONERROR(gckOS_MapPagesEx(os,
2011                 Kernel->core,
2012                 physical,
2013                 pageCount,
2014                 address,
2015                 pageTableEntry));
2016         }
2017
2018         gcmkONERROR(gckMMU_Flush(mmu));
2019     }
2020
2021     *Address = lockInfo->GPUAddresses[Kernel->core] + offset;
2022
2023     gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mapMutex));
2024     acquired = gcvFALSE;
2025
2026
2027     gcmkFOOTER_NO();
2028     return gcvSTATUS_OK;
2029
2030 OnError:
2031     if (acquired)
2032     {
2033         gcmkVERIFY_OK(gckOS_ReleaseMutex(os, Node->mapMutex));
2034     }
2035
2036     gcmkFOOTER();
2037     return status;
2038 }
2039
2040 gceSTATUS
2041 gckVIDMEM_NODE_Unlock(
2042     IN gckKERNEL Kernel,
2043     IN gckVIDMEM_NODE Node,
2044     IN gctUINT32 ProcessID
2045     )
2046 {
2047     gceSTATUS           status;
2048     gcsGPU_MAP_PTR      gpuMap;
2049     gcsLOCK_INFO_PTR    lockInfo;
2050     gckMMU              mmu;
2051     gcuVIDMEM_NODE_PTR  node;
2052     gctUINT32           pageCount;
2053     gctBOOL             acquired = gcvFALSE;
2054
2055     gcmkHEADER_ARG("Kernel=0x%08X, Node = %x, ProcessID=%d",
2056                    Kernel, Node, ProcessID);
2057
2058     gcmkVERIFY_OBJECT(Kernel, gcvOBJ_KERNEL);
2059     gcmkVERIFY_ARGUMENT(Node != gcvNULL);
2060
2061     gcmkONERROR(gckOS_AcquireMutex(Kernel->os, Node->mapMutex, gcvINFINITE));
2062     acquired = gcvTRUE;
2063
2064     /* Get map information for current process. */
2065     gpuMap = _FindGPUMap(Node->mapHead, ProcessID);
2066
2067     if (gpuMap == gcvNULL)
2068     {
2069         /* No mapping for this process. */
2070         gcmkONERROR(gcvSTATUS_INVALID_DATA);
2071     }
2072
2073     lockInfo = &gpuMap->lockInfo;
2074
2075     if (--lockInfo->lockeds[Kernel->core] == 0)
2076     {
2077         node = Node->node;
2078
2079         /* Get necessary information. */
2080         if (node->VidMem.memory->object.type == gcvOBJ_VIDMEM)
2081         {
2082             gctUINT32 phys = node->VidMem.memory->baseAddress
2083                            + node->VidMem.offset
2084                            + node->VidMem.alignment;
2085
2086             /* GPU page table use 4K page. */
2087             pageCount = ((phys + node->VidMem.bytes + 4096 - 1) >> 12)
2088                       - (phys >> 12);
2089         }
2090         else
2091         {
2092             pageCount = node->Virtual.pageCount;
2093         }
2094
2095         /* Get MMU which allocates pages. */
2096         mmu = lockInfo->lockMmus[Kernel->core];
2097
2098         /* Free virtual spaces in page table. */
2099         gcmkVERIFY_OK(gckMMU_FreePagesEx(
2100             mmu,
2101             lockInfo->GPUAddresses[Kernel->core],
2102             pageCount
2103             ));
2104
2105         _DestroyGPUMap(Kernel->os, &Node->mapHead, &Node->mapTail, gpuMap);
2106     }
2107
2108     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Node->mapMutex));
2109     acquired = gcvFALSE;
2110
2111     gcmkFOOTER_NO();
2112     return gcvSTATUS_OK;
2113
2114 OnError:
2115     if (acquired)
2116     {
2117         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, Node->mapMutex));
2118     }
2119
2120     gcmkFOOTER();
2121     return status;
2122 }
2123 #endif
2124
2125 /*******************************************************************************
2126 **
2127 **  gckVIDMEM_HANDLE_Allocate
2128 **
2129 **  Allocate a handle for a gckVIDMEM_NODE object.
2130 **
2131 **  INPUT:
2132 **
2133 **      gckKERNEL Kernel
2134 **          Pointer to an gckKERNEL object.
2135 **
2136 **      gckVIDMEM_NODE Node
2137 **          Pointer to a gckVIDMEM_NODE object.
2138 **
2139 **  OUTPUT:
2140 **
2141 **      gctUINT32 * Handle
2142 **          Pointer to a variable receiving a handle represent this
2143 **          gckVIDMEM_NODE in userspace.
2144 */
2145 static gceSTATUS
2146 gckVIDMEM_HANDLE_Allocate(
2147     IN gckKERNEL Kernel,
2148     IN gckVIDMEM_NODE Node,
2149     OUT gctUINT32 * Handle
2150     )
2151 {
2152     gceSTATUS status;
2153     gctUINT32 processID           = 0;
2154     gctPOINTER pointer            = gcvNULL;
2155     gctPOINTER handleDatabase     = gcvNULL;
2156     gctPOINTER mutex              = gcvNULL;
2157     gctUINT32 handle              = 0;
2158     gckVIDMEM_HANDLE handleObject = gcvNULL;
2159     gckOS os                      = Kernel->os;
2160
2161     gcmkHEADER_ARG("Kernel=0x%X, Node=0x%X", Kernel, Node);
2162
2163     gcmkVERIFY_OBJECT(os, gcvOBJ_OS);
2164
2165     /* Allocate a gckVIDMEM_HANDLE object. */
2166     gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_HANDLE), &pointer));
2167
2168     gcmkVERIFY_OK(gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsVIDMEM_HANDLE)));
2169
2170     handleObject = pointer;
2171
2172     gcmkONERROR(gckOS_AtomConstruct(os, &handleObject->reference));
2173
2174     /* Set default reference count to 1. */
2175     gckOS_AtomSet(os, handleObject->reference, 1);
2176
2177     gcmkVERIFY_OK(gckOS_GetProcessID(&processID));
2178
2179     gcmkONERROR(
2180         gckKERNEL_FindHandleDatbase(Kernel,
2181                                     processID,
2182                                     &handleDatabase,
2183                                     &mutex));
2184
2185     /* Allocate a handle for this object. */
2186     gcmkONERROR(
2187         gckKERNEL_AllocateIntegerId(handleDatabase, handleObject, &handle));
2188
2189     handleObject->node = Node;
2190     handleObject->handle = handle;
2191
2192     *Handle = handle;
2193
2194     gcmkFOOTER_ARG("*Handle=%d", *Handle);
2195     return gcvSTATUS_OK;
2196
2197 OnError:
2198     if (handleObject != gcvNULL)
2199     {
2200         if (handleObject->reference != gcvNULL)
2201         {
2202             gcmkVERIFY_OK(gckOS_AtomDestroy(os, handleObject->reference));
2203         }
2204
2205         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, handleObject));
2206     }
2207
2208     gcmkFOOTER();
2209     return status;
2210 }
2211
2212 static gceSTATUS
2213 gckVIDMEM_NODE_Reference(
2214     IN gckKERNEL Kernel,
2215     IN gckVIDMEM_NODE Node
2216     )
2217 {
2218     gctINT32 oldValue;
2219     gcmkHEADER_ARG("Kernel=0x%X Node=0x%X", Kernel, Node);
2220
2221     gckOS_AtomIncrement(Kernel->os, Node->reference, &oldValue);
2222
2223     gcmkFOOTER_NO();
2224     return gcvSTATUS_OK;
2225 }
2226
2227 gceSTATUS
2228 gckVIDMEM_HANDLE_Reference(
2229     IN gckKERNEL Kernel,
2230     IN gctUINT32 ProcessID,
2231     IN gctUINT32 Handle
2232     )
2233 {
2234     gceSTATUS status;
2235     gckVIDMEM_HANDLE handleObject = gcvNULL;
2236     gctPOINTER database           = gcvNULL;
2237     gctPOINTER mutex              = gcvNULL;
2238     gctINT32 oldValue             = 0;
2239     gctBOOL acquired              = gcvFALSE;
2240
2241     gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID);
2242
2243     gcmkONERROR(
2244         gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex));
2245
2246     gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2247     acquired = gcvTRUE;
2248
2249     /* Translate handle to gckVIDMEM_HANDLE object. */
2250     gcmkONERROR(
2251         gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject));
2252
2253     /* Increase the reference count. */
2254     gckOS_AtomIncrement(Kernel->os, handleObject->reference, &oldValue);
2255
2256     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2257     acquired = gcvFALSE;
2258
2259     gcmkFOOTER_NO();
2260     return gcvSTATUS_OK;
2261
2262 OnError:
2263     if (acquired)
2264     {
2265         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2266     }
2267
2268     gcmkFOOTER();
2269     return status;
2270 }
2271
2272 gceSTATUS
2273 gckVIDMEM_HANDLE_Dereference(
2274     IN gckKERNEL Kernel,
2275     IN gctUINT32 ProcessID,
2276     IN gctUINT32 Handle
2277     )
2278 {
2279     gceSTATUS status;
2280     gctPOINTER handleDatabase     = gcvNULL;
2281     gctPOINTER mutex              = gcvNULL;
2282     gctINT32 oldValue             = 0;
2283     gckVIDMEM_HANDLE handleObject = gcvNULL;
2284     gctBOOL acquired              = gcvFALSE;
2285
2286     gcmkHEADER_ARG("Handle=%d PrcoessID=%d", Handle, ProcessID);
2287
2288     gcmkONERROR(
2289         gckKERNEL_FindHandleDatbase(Kernel,
2290                                     ProcessID,
2291                                     &handleDatabase,
2292                                     &mutex));
2293
2294     gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2295     acquired = gcvTRUE;
2296
2297     /* Translate handle to gckVIDMEM_HANDLE. */
2298     gcmkONERROR(
2299         gckKERNEL_QueryIntegerId(handleDatabase, Handle, (gctPOINTER *)&handleObject));
2300
2301     gckOS_AtomDecrement(Kernel->os, handleObject->reference, &oldValue);
2302
2303     if (oldValue == 1)
2304     {
2305         /* Remove handle from database if this is the last reference. */
2306         gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(handleDatabase, Handle));
2307     }
2308
2309     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2310     acquired = gcvFALSE;
2311
2312     if (oldValue == 1)
2313     {
2314         gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, handleObject->reference));
2315         gcmkOS_SAFE_FREE(Kernel->os, handleObject);
2316     }
2317
2318     gcmkFOOTER_NO();
2319     return gcvSTATUS_OK;
2320
2321 OnError:
2322     if (acquired)
2323     {
2324         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2325     }
2326
2327     gcmkFOOTER();
2328     return status;
2329 }
2330
2331 gceSTATUS
2332 gckVIDMEM_HANDLE_LookupAndReference(
2333     IN gckKERNEL Kernel,
2334     IN gctUINT32 Handle,
2335     OUT gckVIDMEM_NODE * Node
2336     )
2337 {
2338     gceSTATUS status;
2339     gckVIDMEM_HANDLE handleObject = gcvNULL;
2340     gckVIDMEM_NODE node           = gcvNULL;
2341     gctPOINTER database           = gcvNULL;
2342     gctPOINTER mutex              = gcvNULL;
2343     gctUINT32 processID           = 0;
2344     gctBOOL acquired              = gcvFALSE;
2345
2346     gcmkHEADER_ARG("Kernel=0x%X Handle=%d", Kernel, Handle);
2347
2348     gckOS_GetProcessID(&processID);
2349
2350     gcmkONERROR(
2351         gckKERNEL_FindHandleDatbase(Kernel, processID, &database, &mutex));
2352
2353     gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2354     acquired = gcvTRUE;
2355
2356     /* Translate handle to gckVIDMEM_HANDLE object. */
2357     gcmkONERROR(
2358         gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject));
2359
2360     /* Get gckVIDMEM_NODE object. */
2361     node = handleObject->node;
2362
2363     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2364     acquired = gcvFALSE;
2365
2366     /* Reference this gckVIDMEM_NODE object. */
2367     gcmkVERIFY_OK(gckVIDMEM_NODE_Reference(Kernel, node));
2368
2369     /* Return result. */
2370     *Node = node;
2371
2372     gcmkFOOTER_ARG("*Node=%d", *Node);
2373     return gcvSTATUS_OK;
2374
2375 OnError:
2376     if (acquired)
2377     {
2378         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2379     }
2380
2381     gcmkFOOTER();
2382     return status;
2383 }
2384
2385 gceSTATUS
2386 gckVIDMEM_HANDLE_Lookup(
2387     IN gckKERNEL Kernel,
2388     IN gctUINT32 ProcessID,
2389     IN gctUINT32 Handle,
2390     OUT gckVIDMEM_NODE * Node
2391     )
2392 {
2393     gceSTATUS status;
2394     gckVIDMEM_HANDLE handleObject = gcvNULL;
2395     gckVIDMEM_NODE node           = gcvNULL;
2396     gctPOINTER database           = gcvNULL;
2397     gctPOINTER mutex              = gcvNULL;
2398     gctBOOL acquired              = gcvFALSE;
2399
2400     gcmkHEADER_ARG("Kernel=0x%X ProcessID=%d Handle=%d",
2401                    Kernel, ProcessID, Handle);
2402
2403     gcmkONERROR(
2404         gckKERNEL_FindHandleDatbase(Kernel, ProcessID, &database, &mutex));
2405
2406     gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2407     acquired = gcvTRUE;
2408
2409     gcmkONERROR(
2410         gckKERNEL_QueryIntegerId(database, Handle, (gctPOINTER *)&handleObject));
2411
2412     node = handleObject->node;
2413
2414     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2415     acquired = gcvFALSE;
2416
2417     *Node = node;
2418
2419     gcmkFOOTER_ARG("*Node=%d", *Node);
2420     return gcvSTATUS_OK;
2421
2422 OnError:
2423     if (acquired)
2424     {
2425         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2426     }
2427
2428     gcmkFOOTER();
2429     return status;
2430 }
2431
2432 /*******************************************************************************
2433 **
2434 **  gckVIDMEM_NODE_Allocate
2435 **
2436 **  Allocate a gckVIDMEM_NODE object.
2437 **
2438 **  INPUT:
2439 **
2440 **      gckKERNEL Kernel
2441 **          Pointer to an gckKERNEL object.
2442 **
2443 **      gcuVIDMEM_NODE_PTR Node
2444 **          Pointer to a gcuVIDMEM_NODE union.
2445 **
2446 **  OUTPUT:
2447 **
2448 **      gctUINT32 * Handle
2449 **          Pointer to a variable receiving a handle represent this
2450 **          gckVIDMEM_NODE in userspace.
2451 */
2452 gceSTATUS
2453 gckVIDMEM_NODE_Allocate(
2454     IN gckKERNEL Kernel,
2455     IN gcuVIDMEM_NODE_PTR VideoNode,
2456     IN gceSURF_TYPE Type,
2457     IN gcePOOL Pool,
2458     IN gctUINT32 * Handle
2459     )
2460 {
2461     gceSTATUS status;
2462     gckVIDMEM_NODE node = gcvNULL;
2463     gctPOINTER pointer  = gcvNULL;
2464     gctUINT32 handle    = 0;
2465     gckOS os            = Kernel->os;
2466
2467     gcmkHEADER_ARG("Kernel=0x%X VideoNode=0x%X", Kernel, VideoNode);
2468
2469     /* Construct a node. */
2470     gcmkONERROR(gckOS_Allocate(os, gcmSIZEOF(gcsVIDMEM_NODE), &pointer));
2471
2472     gcmkVERIFY_OK(gckOS_ZeroMemory(pointer, gcmSIZEOF(gcsVIDMEM_NODE)));
2473
2474     node = pointer;
2475
2476     node->node = VideoNode;
2477     node->type = Type;
2478     node->pool = Pool;
2479
2480 #if gcdPROCESS_ADDRESS_SPACE
2481     gcmkONERROR(gckOS_CreateMutex(os, &node->mapMutex));
2482 #endif
2483
2484     gcmkONERROR(gckOS_AtomConstruct(os, &node->reference));
2485
2486     gcmkONERROR(gckOS_CreateMutex(os, &node->mutex));
2487
2488     /* Reference is 1 by default . */
2489     gckVIDMEM_NODE_Reference(Kernel, node);
2490
2491     /* Create a handle to represent this node. */
2492     gcmkONERROR(gckVIDMEM_HANDLE_Allocate(Kernel, node, &handle));
2493
2494     *Handle = handle;
2495
2496     gcmkFOOTER_ARG("*Handle=%d", *Handle);
2497     return gcvSTATUS_OK;
2498
2499 OnError:
2500     if (node != gcvNULL)
2501     {
2502 #if gcdPROCESS_ADDRESS_SPACE
2503         if (node->mapMutex != gcvNULL)
2504         {
2505             gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->mapMutex));
2506         }
2507 #endif
2508
2509         if (node->mutex)
2510         {
2511             gcmkVERIFY_OK(gckOS_DeleteMutex(os, node->mutex));
2512         }
2513
2514         if (node->reference != gcvNULL)
2515         {
2516             gcmkVERIFY_OK(gckOS_AtomDestroy(os, node->reference));
2517         }
2518
2519         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(os, node));
2520     }
2521
2522     gcmkFOOTER();
2523     return status;
2524 }
2525
2526 gceSTATUS
2527 gckVIDMEM_NODE_Dereference(
2528     IN gckKERNEL Kernel,
2529     IN gckVIDMEM_NODE Node
2530     )
2531 {
2532     gctINT32 oldValue   = 0;
2533     gctPOINTER database = Kernel->db->nameDatabase;
2534     gctPOINTER mutex    = Kernel->db->nameDatabaseMutex;
2535
2536     gcmkHEADER_ARG("Kernel=0x%X Node=0x%X", Kernel, Node);
2537
2538     gcmkVERIFY_OK(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2539
2540     gcmkVERIFY_OK(gckOS_AtomDecrement(Kernel->os, Node->reference, &oldValue));
2541
2542     if (oldValue == 1 && Node->name)
2543     {
2544         /* Free name if exists. */
2545         gcmkVERIFY_OK(gckKERNEL_FreeIntegerId(database, Node->name));
2546     }
2547
2548     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2549
2550     if (oldValue == 1)
2551     {
2552         /* Free gcuVIDMEM_NODE. */
2553         gcmkVERIFY_OK(gckVIDMEM_Free(Kernel, Node->node));
2554         gcmkVERIFY_OK(gckOS_AtomDestroy(Kernel->os, Node->reference));
2555 #if gcdPROCESS_ADDRESS_SPACE
2556         gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Node->mapMutex));
2557 #endif
2558         gcmkVERIFY_OK(gckOS_DeleteMutex(Kernel->os, Node->mutex));
2559         gcmkOS_SAFE_FREE(Kernel->os, Node);
2560     }
2561
2562     gcmkFOOTER_NO();
2563     return gcvSTATUS_OK;
2564 }
2565
2566 /*******************************************************************************
2567 **
2568 **  gckVIDMEM_NODE_Name
2569 **
2570 **  Naming a gckVIDMEM_NODE object.
2571 **
2572 **  INPUT:
2573 **
2574 **      gckKERNEL Kernel
2575 **          Pointer to an gckKERNEL object.
2576 **
2577 **      gctUINT32 Handle
2578 **          Handle to a gckVIDMEM_NODE object.
2579 **
2580 **  OUTPUT:
2581 **
2582 **      gctUINT32 * Name
2583 **          Pointer to a variable receiving a name which can be pass to another
2584 **          process.
2585 */
2586 gceSTATUS
2587 gckVIDMEM_NODE_Name(
2588     IN gckKERNEL Kernel,
2589     IN gctUINT32 Handle,
2590     IN gctUINT32 * Name
2591     )
2592 {
2593     gceSTATUS status;
2594     gckVIDMEM_NODE node = gcvNULL;
2595     gctUINT32 name      = 0;
2596     gctUINT32 processID = 0;
2597     gctPOINTER database = Kernel->db->nameDatabase;
2598     gctPOINTER mutex    = Kernel->db->nameDatabaseMutex;
2599     gctBOOL acquired    = gcvFALSE;
2600     gctBOOL referenced  = gcvFALSE;
2601     gcmkHEADER_ARG("Kernel=0x%X Handle=%d", Kernel, Handle);
2602
2603     gcmkONERROR(gckOS_GetProcessID(&processID));
2604
2605     gcmkONERROR(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2606     acquired = gcvTRUE;
2607
2608     gcmkONERROR(gckVIDMEM_HANDLE_LookupAndReference(Kernel, Handle, &node));
2609     referenced = gcvTRUE;
2610
2611     if (node->name == 0)
2612     {
2613         /* Name this node. */
2614         gcmkONERROR(gckKERNEL_AllocateIntegerId(database, node, &name));
2615         node->name = name;
2616     }
2617
2618     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2619     acquired = gcvFALSE;
2620
2621     gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node));
2622
2623     if(node)
2624     {
2625         *Name = node->name;
2626     }
2627
2628     gcmkFOOTER_ARG("*Name=%d", *Name);
2629     return gcvSTATUS_OK;
2630
2631 OnError:
2632     if (referenced)
2633     {
2634         gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node));
2635     }
2636
2637     if (acquired)
2638     {
2639         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2640     }
2641
2642     gcmkFOOTER();
2643     return status;
2644 }
2645
2646 /*******************************************************************************
2647 **
2648 **  gckVIDMEM_NODE_Import
2649 **
2650 **  Import a gckVIDMEM_NODE object.
2651 **
2652 **  INPUT:
2653 **
2654 **      gckKERNEL Kernel
2655 **          Pointer to an gckKERNEL object.
2656 **
2657 **      gctUINT32 Name
2658 **          Name of a gckVIDMEM_NODE object.
2659 **
2660 **  OUTPUT:
2661 **
2662 **      gctUINT32 * Handle
2663 **          Pointer to a variable receiving a handle represent this
2664 **          gckVIDMEM_NODE in userspace.
2665 */
2666 gceSTATUS
2667 gckVIDMEM_NODE_Import(
2668     IN gckKERNEL Kernel,
2669     IN gctUINT32 Name,
2670     IN gctUINT32 * Handle
2671     )
2672 {
2673     gceSTATUS status;
2674     gckVIDMEM_NODE node = gcvNULL;
2675     gctPOINTER database = Kernel->db->nameDatabase;
2676     gctPOINTER mutex    = Kernel->db->nameDatabaseMutex;
2677     gctBOOL acquired    = gcvFALSE;
2678     gctBOOL referenced  = gcvFALSE;
2679
2680     gcmkHEADER_ARG("Kernel=0x%X Name=%d", Kernel, Name);
2681
2682     gcmkONERROR(gckOS_AcquireMutex(Kernel->os, mutex, gcvINFINITE));
2683     acquired = gcvTRUE;
2684
2685     /* Lookup in database to get the node. */
2686     gcmkONERROR(gckKERNEL_QueryIntegerId(database, Name, (gctPOINTER *)&node));
2687
2688     /* Reference the node. */
2689     gcmkONERROR(gckVIDMEM_NODE_Reference(Kernel, node));
2690     referenced = gcvTRUE;
2691
2692     gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2693     acquired = gcvFALSE;
2694
2695     /* Allocate a handle for current process. */
2696     gcmkONERROR(gckVIDMEM_HANDLE_Allocate(Kernel, node, Handle));
2697
2698     gcmkFOOTER_ARG("*Handle=%d", *Handle);
2699     return gcvSTATUS_OK;
2700
2701 OnError:
2702     if (referenced)
2703     {
2704         gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node));
2705     }
2706
2707     if (acquired)
2708     {
2709         gcmkVERIFY_OK(gckOS_ReleaseMutex(Kernel->os, mutex));
2710     }
2711
2712     gcmkFOOTER();
2713     return status;
2714 }
2715
2716
2717 typedef struct _gcsVIDMEM_NODE_FDPRIVATE
2718 {
2719     gcsFDPRIVATE   base;
2720     gckKERNEL      kernel;
2721     gckVIDMEM_NODE node;
2722 }
2723 gcsVIDMEM_NODE_FDPRIVATE;
2724
2725
2726 static gctINT
2727 _ReleaseFdPrivate(
2728     gcsFDPRIVATE_PTR FdPrivate
2729     )
2730 {
2731     /* Cast private info. */
2732     gcsVIDMEM_NODE_FDPRIVATE * private = (gcsVIDMEM_NODE_FDPRIVATE *) FdPrivate;
2733
2734     gckVIDMEM_NODE_Dereference(private->kernel, private->node);
2735     gckOS_Free(private->kernel->os, private);
2736
2737     return 0;
2738 }
2739
2740 /*******************************************************************************
2741 **
2742 **  gckVIDMEM_NODE_GetFd
2743 **
2744 **  Attach a gckVIDMEM_NODE object to a native fd.
2745 **
2746 **  INPUT:
2747 **
2748 **      gckKERNEL Kernel
2749 **          Pointer to an gckKERNEL object.
2750 **
2751 **      gctUINT32 Handle
2752 **          Handle to a gckVIDMEM_NODE object.
2753 **
2754 **  OUTPUT:
2755 **
2756 **      gctUINT32 * Fd
2757 **          Pointer to a variable receiving a native fd from os.
2758 */
2759 gceSTATUS
2760 gckVIDMEM_NODE_GetFd(
2761     IN gckKERNEL Kernel,
2762     IN gctUINT32 Handle,
2763     OUT gctINT * Fd
2764     )
2765 {
2766     gceSTATUS status;
2767     gckVIDMEM_NODE node = gcvNULL;
2768     gctBOOL referenced  = gcvFALSE;
2769     gcsVIDMEM_NODE_FDPRIVATE * fdPrivate = gcvNULL;
2770     gcmkHEADER_ARG("Kernel=0x%X Handle=%d", Kernel, Handle);
2771
2772     /* Query and reference handle. */
2773     gcmkONERROR(gckVIDMEM_HANDLE_LookupAndReference(Kernel, Handle, &node));
2774     referenced = gcvTRUE;
2775
2776     /* Allocate memory for private info. */
2777     gcmkONERROR(gckOS_Allocate(
2778         Kernel->os,
2779         gcmSIZEOF(gcsVIDMEM_NODE_FDPRIVATE),
2780         (gctPOINTER *)&fdPrivate
2781         ));
2782
2783     fdPrivate->base.release = _ReleaseFdPrivate;
2784     fdPrivate->kernel = Kernel;
2785     fdPrivate->node   = node;
2786
2787     /* Allocated fd owns a reference. */
2788     gcmkONERROR(gckOS_GetFd("vidmem", &fdPrivate->base, Fd));
2789
2790     gcmkFOOTER_ARG("*Fd=%d", *Fd);
2791     return gcvSTATUS_OK;
2792
2793 OnError:
2794     if (referenced)
2795     {
2796         gcmkVERIFY_OK(gckVIDMEM_NODE_Dereference(Kernel, node));
2797     }
2798
2799     if (fdPrivate)
2800     {
2801         gcmkVERIFY_OK(gcmkOS_SAFE_FREE(Kernel->os, fdPrivate));
2802     }
2803
2804     gcmkFOOTER();
2805     return status;
2806 }
2807