1/**************************************************************************
2
3Copyright 1998-1999 Precision Insight, Inc., Cedar Park, Texas.
4All Rights Reserved.
5
6Permission is hereby granted, free of charge, to any person obtaining a
7copy of this software and associated documentation files (the
8"Software"), to deal in the Software without restriction, including
9without limitation the rights to use, copy, modify, merge, publish,
10distribute, sub license, and/or sell copies of the Software, and to
11permit persons to whom the Software is furnished to do so, subject to
12the following conditions:
13
14The above copyright notice and this permission notice (including the
15next paragraph) shall be included in all copies or substantial portions
16of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
22ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26**************************************************************************/
27
28#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
31
32/*
33 * Authors:
34 *   Keith Whitwell <keith@tungstengraphics.com>
35 *
36 */
37
38#include "xf86.h"
39#include "xf86_OSproc.h"
40
41#include "i810.h"
42#include "i810_reg.h"
43
44int
45I810AllocLow(I810MemRange * result, I810MemRange * pool, int size)
46{
47   if (size > (long)pool->Size)
48      return 0;
49
50   pool->Size -= size;
51   result->Size = size;
52   result->Start = pool->Start;
53   result->End = pool->Start += size;
54
55   return 1;
56}
57
58int
59I810AllocHigh(I810MemRange * result, I810MemRange * pool, int size)
60{
61   if (size > (long)pool->Size)
62      return 0;
63
64   pool->Size -= size;
65   result->Size = size;
66   result->End = pool->End;
67   result->Start = pool->End -= size;
68
69   return 1;
70}
71
72int
73I810AllocateGARTMemory(ScrnInfoPtr pScrn)
74{
75   unsigned long size = pScrn->videoRam * 1024;
76   I810Ptr pI810 = I810PTR(pScrn);
77   int key;
78   long tom = 0;
79   unsigned long physical;
80
81   if (!xf86AgpGARTSupported() || !xf86AcquireGART(pScrn->scrnIndex)) {
82      xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
83		 "AGP GART support is either not available or cannot be used.\n"
84		 "\tMake sure your kernel has agpgart support or has the\n"
85		 "\tagpgart module loaded.\n");
86      return FALSE;
87   }
88
89   /* This allows the 2d only Xserver to regen */
90   pI810->agpAcquired2d = TRUE;
91
92   /*
93    * I810/I815
94    *
95    * Treat the gart like video memory - we assume we own all that is
96    * there, so ignore EBUSY errors.  Don't try to remove it on
97    * failure, either, as other X server may be using it.
98    */
99
100   if ((key = xf86AllocateGARTMemory(pScrn->scrnIndex, size, 0, NULL)) == -1)
101      return FALSE;
102
103   pI810->VramOffset = 0;
104   pI810->VramKey = key;
105
106   if (!xf86BindGARTMemory(pScrn->scrnIndex, key, 0))
107      return FALSE;
108
109   pI810->SysMem.Start = 0;
110   pI810->SysMem.Size = size;
111   pI810->SysMem.End = size;
112   pI810->SavedSysMem = pI810->SysMem;
113
114   tom = pI810->SysMem.End;
115
116   pI810->DcacheMem.Start = 0;
117   pI810->DcacheMem.End = 0;
118   pI810->DcacheMem.Size = 0;
119   pI810->CursorPhysical = 0;
120   pI810->CursorARGBPhysical = 0;
121
122   /*
123    * Dcache - half the speed of normal ram, so not really useful for
124    * a 2d server.  Don't bother reporting its presence.  This is
125    * mapped in addition to the requested amount of system ram.
126    */
127
128   size = 1024 * 4096;
129
130   /*
131    * Keep it 512K aligned for the sake of tiled regions.
132    */
133
134   tom += 0x7ffff;
135   tom &= ~0x7ffff;
136
137   if ((key = xf86AllocateGARTMemory(pScrn->scrnIndex, size, 1, NULL)) != -1) {
138      pI810->DcacheOffset = tom;
139      pI810->DcacheKey = key;
140      if (!xf86BindGARTMemory(pScrn->scrnIndex, key, tom)) {
141	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
142		    "Allocation of %ld bytes for DCACHE failed\n", size);
143	 pI810->DcacheKey = -1;
144      } else {
145	 pI810->DcacheMem.Start = tom;
146	 pI810->DcacheMem.Size = size;
147	 pI810->DcacheMem.End = pI810->DcacheMem.Start + pI810->DcacheMem.Size;
148	 tom = pI810->DcacheMem.End;
149      }
150   } else {
151      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
152		 "No physical memory available for %ld bytes of DCACHE\n",
153		 size);
154      pI810->DcacheKey = -1;
155   }
156
157   /*
158    * Mouse cursor -- The i810 (crazy) needs a physical address in
159    * system memory from which to upload the cursor.  We get this from
160    * the agpgart module using a special memory type.
161    */
162
163   /*
164    * 4k for the cursor is excessive, I'm going to steal 3k for
165    * overlay registers later
166    */
167
168   size = 4096;
169
170   if ((key =
171	xf86AllocateGARTMemory(pScrn->scrnIndex, size, 2, &physical)) == -1) {
172      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
173		 "No physical memory available for HW cursor\n");
174      pI810->HwcursKey = -1;
175      pI810->CursorStart = 0;
176   } else {
177      pI810->HwcursOffset = tom;
178      pI810->HwcursKey = key;
179      if (!xf86BindGARTMemory(pScrn->scrnIndex, key, tom)) {
180	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
181		    "Allocation of %ld bytes for HW cursor failed\n", size);
182	 pI810->HwcursKey = -1;
183      } else {
184	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
185		    "Allocated of %ld bytes for HW cursor\n", size);
186	 pI810->CursorPhysical = physical;
187	 pI810->CursorStart = tom;
188	 tom += size;
189      }
190   }
191
192   /*
193    * 16k for the ARGB cursor
194    */
195
196   size = 16384;
197
198   if ((key =
199	xf86AllocateGARTMemory(pScrn->scrnIndex, size, 2, &physical)) == -1) {
200      xf86DrvMsg(pScrn->scrnIndex, X_INFO,
201		 "No physical memory available for ARGB HW cursor\n");
202      pI810->ARGBHwcursKey = -1;
203      pI810->CursorARGBStart = 0;
204   } else {
205      pI810->ARGBHwcursOffset = tom;
206      pI810->ARGBHwcursKey = key;
207      if (!xf86BindGARTMemory(pScrn->scrnIndex, key, tom)) {
208	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
209		    "Allocation of %ld bytes for ARGB HW cursor failed\n", size);
210	 pI810->ARGBHwcursKey = -1;
211      } else {
212	 xf86DrvMsg(pScrn->scrnIndex, X_INFO,
213		    "Allocated of %ld bytes for ARGB HW cursor\n", size);
214	 pI810->CursorARGBPhysical = physical;
215	 pI810->CursorARGBStart = tom;
216	 tom += size;
217      }
218   }
219
220   /*
221    * Overlay register buffer -- Just like the cursor, the i810 needs a
222    * physical address in system memory from which to upload the overlay
223    * registers.
224    */
225
226   if (pI810->CursorStart != 0) {
227      pI810->OverlayPhysical = pI810->CursorPhysical + 1024;
228      pI810->OverlayStart = pI810->CursorStart + 1024;
229   }
230
231   pI810->GttBound = 1;
232
233   return TRUE;
234}
235
236/* Tiled memory is good... really, really good...
237 *
238 * Need to make it less likely that we miss out on this - probably
239 * need to move the frontbuffer away from the 'guarenteed' alignment
240 * of the first memory segment, or perhaps allocate a discontigous
241 * framebuffer to get more alignment 'sweet spots'.
242 */
243void
244I810SetTiledMemory(ScrnInfoPtr pScrn, int nr, unsigned int start,
245		   unsigned int pitch, unsigned int size)
246{
247   I810Ptr pI810 = I810PTR(pScrn);
248   I810RegPtr i810Reg = &pI810->ModeReg;
249   uint32_t val;
250   uint32_t fence_mask = 0;
251
252   if (nr < 0 || nr > 7) {
253      xf86DrvMsg(pScrn->scrnIndex, X_WARNING, "%s - fence %d out of range\n",
254		 "I810SetTiledMemory", nr);
255      return;
256   }
257
258   i810Reg->Fence[nr] = 0;
259
260   fence_mask = ~FENCE_START_MASK;
261
262   if (start & fence_mask) {
263      xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
264		 "%s %d: start (%x) is not 512k aligned\n",
265		 "I810SetTiledMemory", nr, start);
266      return;
267   }
268
269   if (start % size) {
270      xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
271		 "%s %d: start (%x) is not size (%x) aligned\n",
272		 "I810SetTiledMemory", nr, start, size);
273      return;
274   }
275
276   if (pitch & 127) {
277      xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
278		 "%s %d: pitch (%x) not a multiple of 128 bytes\n",
279		 "I810SetTiledMemory", nr, pitch);
280      return;
281   }
282
283   val = (start | FENCE_X_MAJOR | FENCE_VALID);
284
285   switch (size) {
286   case KB(512):
287      val |= FENCE_SIZE_512K;
288      break;
289   case MB(1):
290      val |= FENCE_SIZE_1M;
291      break;
292   case MB(2):
293      val |= FENCE_SIZE_2M;
294      break;
295   case MB(4):
296      val |= FENCE_SIZE_4M;
297      break;
298   case MB(8):
299      val |= FENCE_SIZE_8M;
300      break;
301   case MB(16):
302      val |= FENCE_SIZE_16M;
303      break;
304   case MB(32):
305      val |= FENCE_SIZE_32M;
306      break;
307   default:
308      xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
309		 "%s %d: illegal size (0x%x)\n", "I810SetTiledMemory", nr,
310		 size);
311      return;
312   }
313
314   switch (pitch / 128) {
315   case 1:
316      val |= FENCE_PITCH_1;
317      break;
318   case 2:
319      val |= FENCE_PITCH_2;
320      break;
321   case 4:
322      val |= FENCE_PITCH_4;
323      break;
324   case 8:
325      val |= FENCE_PITCH_8;
326      break;
327   case 16:
328      val |= FENCE_PITCH_16;
329      break;
330   case 32:
331      val |= FENCE_PITCH_32;
332      break;
333   default:
334      xf86DrvMsg(pScrn->scrnIndex, X_WARNING,
335		 "%s %d: illegal size (0x%x)\n", "I810SetTiledMemory", nr,
336		 size);
337      return;
338   }
339
340   i810Reg->Fence[nr] = val;
341}
342
343Bool
344I810BindGARTMemory(ScrnInfoPtr pScrn)
345{
346   I810Ptr pI810 = I810PTR(pScrn);
347
348   if (xf86AgpGARTSupported() && !pI810->directRenderingEnabled
349       && !pI810->GttBound) {
350      if (!xf86AcquireGART(pScrn->scrnIndex))
351	 return FALSE;
352
353      if (pI810->VramKey != -1
354	  && !xf86BindGARTMemory(pScrn->scrnIndex, pI810->VramKey,
355				 pI810->VramOffset))
356	 return FALSE;
357
358      if (pI810->DcacheKey != -1
359	  && !xf86BindGARTMemory(pScrn->scrnIndex, pI810->DcacheKey,
360				 pI810->DcacheOffset))
361	 return FALSE;
362
363      if (pI810->HwcursKey != -1
364	  && !xf86BindGARTMemory(pScrn->scrnIndex, pI810->HwcursKey,
365				 pI810->HwcursOffset))
366	 return FALSE;
367
368      if (pI810->ARGBHwcursKey != -1
369	  && !xf86BindGARTMemory(pScrn->scrnIndex, pI810->ARGBHwcursKey,
370				 pI810->ARGBHwcursOffset))
371	 return FALSE;
372
373      pI810->GttBound = 1;
374   }
375
376   return TRUE;
377}
378
379Bool
380I810UnbindGARTMemory(ScrnInfoPtr pScrn)
381{
382   I810Ptr pI810 = I810PTR(pScrn);
383
384   if (xf86AgpGARTSupported() && !pI810->directRenderingEnabled
385       && pI810->GttBound) {
386      if (pI810->VramKey != -1
387	  && !xf86UnbindGARTMemory(pScrn->scrnIndex, pI810->VramKey))
388	 return FALSE;
389
390      if (pI810->DcacheKey != -1
391	  && !xf86UnbindGARTMemory(pScrn->scrnIndex, pI810->DcacheKey))
392	 return FALSE;
393
394      if (pI810->HwcursKey != -1
395	  && !xf86UnbindGARTMemory(pScrn->scrnIndex, pI810->HwcursKey))
396	 return FALSE;
397
398      if (pI810->ARGBHwcursKey != -1
399	  && !xf86UnbindGARTMemory(pScrn->scrnIndex, pI810->ARGBHwcursKey))
400	 return FALSE;
401
402      if (!xf86ReleaseGART(pScrn->scrnIndex))
403	 return FALSE;
404
405      pI810->GttBound = 0;
406   }
407
408   return TRUE;
409}
410
411int
412I810CheckAvailableMemory(ScrnInfoPtr pScrn)
413{
414   AgpInfoPtr agpinf;
415   int maxPages;
416
417   if (!xf86AgpGARTSupported() ||
418       !xf86AcquireGART(pScrn->scrnIndex) ||
419       (agpinf = xf86GetAGPInfo(pScrn->scrnIndex)) == NULL ||
420       !xf86ReleaseGART(pScrn->scrnIndex))
421      return -1;
422
423   maxPages = agpinf->totalPages - agpinf->usedPages;
424   xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, 2, "%s: %dk available\n",
425		  "I810CheckAvailableMemory", maxPages * 4);
426
427   return maxPages * 4;
428}
429