Fix my nv10 cursor.
[nouveau] / src / nv_exa.c
1  /***************************************************************************\
2 |*                                                                           *|
3 |*       Copyright 2003 NVIDIA, Corporation.  All rights reserved.           *|
4 |*                                                                           *|
5 |*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
6 |*     international laws.  Users and possessors of this source code are     *|
7 |*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
8 |*     use this code in individual and commercial software.                  *|
9 |*                                                                           *|
10 |*     Any use of this source code must include,  in the user documenta-     *|
11 |*     tion and  internal comments to the code,  notices to the end user     *|
12 |*     as follows:                                                           *|
13 |*                                                                           *|
14 |*       Copyright 2003 NVIDIA, Corporation.  All rights reserved.           *|
15 |*                                                                           *|
16 |*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
17 |*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
18 |*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
19 |*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
20 |*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
21 |*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
22 |*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
23 |*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
24 |*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
25 |*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
26 |*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
27 |*                                                                           *|
28 |*     U.S. Government  End  Users.   This source code  is a "commercial     *|
29 |*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
30 |*     consisting  of "commercial  computer  software"  and  "commercial     *|
31 |*     computer  software  documentation,"  as such  terms  are  used in     *|
32 |*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
33 |*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
34 |*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
35 |*     all U.S. Government End Users  acquire the source code  with only     *|
36 |*     those rights set forth herein.                                        *|
37 |*                                                                           *|
38  \***************************************************************************/
39
40 /*
41   Exa Modifications (c) Lars Knoll (lars@trolltech.com)
42  */
43
44 #include "nv_include.h"
45 #include "exa.h"
46
47 #include "nv_dma.h"
48 #include "nv_local.h"
49
50 #include <sys/time.h>
51
52 const int NVCopyROP[16] =
53 {
54    0x00,            /* GXclear */
55    0x88,            /* GXand */
56    0x44,            /* GXandReverse */
57    0xCC,            /* GXcopy */
58    0x22,            /* GXandInverted */
59    0xAA,            /* GXnoop */
60    0x66,            /* GXxor */
61    0xEE,            /* GXor */
62    0x11,            /* GXnor */
63    0x99,            /* GXequiv */
64    0x55,            /* GXinvert*/
65    0xDD,            /* GXorReverse */
66    0x33,            /* GXcopyInverted */
67    0xBB,            /* GXorInverted */
68    0x77,            /* GXnand */
69    0xFF             /* GXset */
70 };
71
72 static void 
73 NVSetPattern(ScrnInfoPtr pScrn, CARD32 clr0, CARD32 clr1,
74                                 CARD32 pat0, CARD32 pat1)
75 {
76         NVPtr pNv = NVPTR(pScrn);
77
78         BEGIN_RING(NvImagePattern, NV04_IMAGE_PATTERN_MONOCHROME_COLOR0, 4);
79         OUT_RING  (clr0);
80         OUT_RING  (clr1);
81         OUT_RING  (pat0);
82         OUT_RING  (pat1);
83 }
84
85 static void 
86 NVSetROP(ScrnInfoPtr pScrn, CARD32 alu, CARD32 planemask)
87 {
88         NVPtr pNv = NVPTR(pScrn);
89         int rop = NVCopyROP[alu] & 0xf0;
90
91         if (planemask != ~0) {
92                 NVSetPattern(pScrn, 0, planemask, ~0, ~0);
93                 if (pNv->currentRop != (alu + 32)) {
94                         BEGIN_RING(NvRop, NV03_CONTEXT_ROP_ROP, 1);
95                         OUT_RING  (rop | 0x0a);
96                         pNv->currentRop = alu + 32;
97                 }
98         } else
99         if (pNv->currentRop != alu) {
100                 if(pNv->currentRop >= 16)
101                         NVSetPattern(pScrn, ~0, ~0, ~0, ~0);
102                 BEGIN_RING(NvRop, NV03_CONTEXT_ROP_ROP, 1);
103                 OUT_RING  (rop | (rop >> 4));
104                 pNv->currentRop = alu;
105         }
106 }
107
108 static CARD32 rectFormat(DrawablePtr pDrawable)
109 {
110         switch(pDrawable->bitsPerPixel) {
111         case 32:
112         case 24:
113                 return NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT_A8R8G8B8;
114                 break;
115         case 16:
116                 return NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT_A16R5G6B5;
117                 break;
118         default:
119                 return NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT_A8R8G8B8;
120                 break;
121         }
122 }
123
124 /* EXA acceleration hooks */
125 static void NVExaWaitMarker(ScreenPtr pScreen, int marker)
126 {
127         NVSync(xf86Screens[pScreen->myNum]);
128 }
129
130 static Bool NVExaPrepareSolid(PixmapPtr pPixmap,
131                               int   alu,
132                               Pixel planemask,
133                               Pixel fg)
134 {
135         ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
136         NVPtr pNv = NVPTR(pScrn);
137         unsigned int fmt, pitch;
138
139         planemask |= ~0 << pPixmap->drawable.bitsPerPixel;
140         if (planemask != ~0 || alu != GXcopy) {
141                 if (pPixmap->drawable.bitsPerPixel == 32)
142                         return FALSE;
143                 BEGIN_RING(NvRectangle, NV04_GDI_RECTANGLE_TEXT_OPERATION, 1);
144                 OUT_RING  (1); /* ROP_AND */
145                 NVSetROP(pScrn, alu, planemask);
146         } else {
147                 BEGIN_RING(NvRectangle, NV04_GDI_RECTANGLE_TEXT_OPERATION, 1);
148                 OUT_RING  (3); /* SRCCOPY */
149         }
150
151         if (!NVAccelGetCtxSurf2DFormatFromPixmap(pPixmap, (int*)&fmt))
152                 return FALSE;
153         pitch = exaGetPixmapPitch(pPixmap);
154
155         /* When SURFACE_FORMAT_A8R8G8B8 is used with GDI_RECTANGLE_TEXT, the 
156          * alpha channel gets forced to 0xFF for some reason.  We're using 
157          * SURFACE_FORMAT_Y32 as a workaround
158          */
159         if (fmt == NV04_CONTEXT_SURFACES_2D_FORMAT_A8R8G8B8)
160                 fmt = NV04_CONTEXT_SURFACES_2D_FORMAT_Y32;
161
162         BEGIN_RING(NvContextSurfaces, NV04_CONTEXT_SURFACES_2D_FORMAT, 4);
163         OUT_RING  (fmt);
164         OUT_RING  ((pitch << 16) | pitch);
165         OUT_PIXMAPl(pPixmap, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
166         OUT_PIXMAPl(pPixmap, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
167
168         BEGIN_RING(NvRectangle, NV04_GDI_RECTANGLE_TEXT_COLOR_FORMAT, 1);
169         OUT_RING  (rectFormat(&pPixmap->drawable));
170         BEGIN_RING(NvRectangle, NV04_GDI_RECTANGLE_TEXT_COLOR1_A, 1);
171         OUT_RING  (fg);
172
173         return TRUE;
174 }
175
176 static void NVExaSolid (PixmapPtr pPixmap, int x1, int y1, int x2, int y2)
177 {
178         ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
179         NVPtr pNv = NVPTR(pScrn);
180         int width = x2-x1;
181         int height = y2-y1;
182
183         BEGIN_RING(NvRectangle,
184                    NV04_GDI_RECTANGLE_TEXT_UNCLIPPED_RECTANGLE_POINT(0), 2);
185         OUT_RING  ((x1 << 16) | y1);
186         OUT_RING  ((width << 16) | height);
187
188         if((width * height) >= 512)
189                 FIRE_RING();
190 }
191
192 static void NVExaDoneSolid (PixmapPtr pPixmap)
193 {
194 }
195
196 static Bool NVExaPrepareCopy(PixmapPtr pSrcPixmap,
197                              PixmapPtr pDstPixmap,
198                              int       dx,
199                              int       dy,
200                              int       alu,
201                              Pixel     planemask)
202 {
203         ScrnInfoPtr pScrn = xf86Screens[pSrcPixmap->drawable.pScreen->myNum];
204         NVPtr pNv = NVPTR(pScrn);
205         int fmt;
206
207         if (pSrcPixmap->drawable.bitsPerPixel !=
208                         pDstPixmap->drawable.bitsPerPixel)
209                 return FALSE;
210
211         planemask |= ~0 << pDstPixmap->drawable.bitsPerPixel;
212         if (planemask != ~0 || alu != GXcopy) {
213                 if (pDstPixmap->drawable.bitsPerPixel == 32)
214                         return FALSE;
215                 BEGIN_RING(NvImageBlit, NV04_IMAGE_BLIT_OPERATION, 1);
216                 OUT_RING  (1); /* ROP_AND */
217                 NVSetROP(pScrn, alu, planemask);
218         } else {
219                 BEGIN_RING(NvImageBlit, NV04_IMAGE_BLIT_OPERATION, 1);
220                 OUT_RING  (3); /* SRCCOPY */
221         }
222
223         if (!NVAccelGetCtxSurf2DFormatFromPixmap(pDstPixmap, &fmt))
224                 return FALSE;
225
226         BEGIN_RING(NvContextSurfaces, NV04_CONTEXT_SURFACES_2D_FORMAT, 4);
227         OUT_RING  (fmt);
228         OUT_RING  ((exaGetPixmapPitch(pDstPixmap) << 16) |
229                    (exaGetPixmapPitch(pSrcPixmap)));
230         OUT_PIXMAPl(pSrcPixmap, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
231         OUT_PIXMAPl(pDstPixmap, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
232
233         return TRUE;
234 }
235
236 static void NVExaCopy(PixmapPtr pDstPixmap,
237                       int       srcX,
238                       int       srcY,
239                       int       dstX,
240                       int       dstY,
241                       int       width,
242                       int       height)
243 {
244         ScrnInfoPtr pScrn = xf86Screens[pDstPixmap->drawable.pScreen->myNum];
245         NVPtr pNv = NVPTR(pScrn);
246
247         /* We want to catch people who have this bug, to find a decent fix */
248 #if 0
249         /* Now check whether we have the same values for srcY and dstY and
250            whether the used chipset is buggy. Currently we flag all of G70
251            cards as buggy, which is probably much to broad. KoalaBR 
252            16 is an abritrary threshold. It should define the maximum number
253            of lines between dstY and srcY  If the number of lines is below
254            we guess, that the bug won't trigger...
255          */
256         if ( ((abs(srcY - dstY)< 16)||(abs(srcX-dstX)<16)) &&
257                 ((((pNv->Chipset & 0xfff0) == CHIPSET_G70) ||
258                  ((pNv->Chipset & 0xfff0) == CHIPSET_G71) ||
259                  ((pNv->Chipset & 0xfff0) == CHIPSET_G72) ||
260                  ((pNv->Chipset & 0xfff0) == CHIPSET_G73) ||
261                  ((pNv->Chipset & 0xfff0) == CHIPSET_C512))) )
262         {
263                 int dx=abs(srcX - dstX),dy=abs(srcY - dstY);
264                 // Ok, let's do it manually unless someone comes up with a better idea
265                 // 1. If dstY and srcY are really the same, do a copy rowwise
266                 if (dy<dx) {
267                         int i,xpos,inc;
268                         NVDEBUG("ExaCopy: Lines identical:\n");
269                         if (srcX>=dstX) {
270                                 xpos=0;
271                                 inc=1;
272                         } else {
273                                 xpos=width-1;
274                                 inc=-1;
275                         }
276                         for (i = 0; i < width; i++) {
277                                 BEGIN_RING(NvImageBlit,
278                                            NV_IMAGE_BLIT_POINT_IN, 3);
279                                 OUT_RING  ((srcY << 16) | (srcX+xpos));
280                                 OUT_RING  ((dstY << 16) | (dstX+xpos));
281                                 OUT_RING  ((height  << 16) | 1);
282                                 xpos+=inc;
283                         }
284                 } else {
285                         // 2. Otherwise we will try a line by line copy in the hope to avoid
286                         //    the card's bug.
287                         int i,ypos,inc;
288                         NVDEBUG("ExaCopy: Lines nearly the same srcY=%d, dstY=%d:\n", srcY, dstY);
289                         if (srcY>=dstY) {
290                                 ypos=0;
291                                 inc=1;
292                         } else {
293                                 ypos=height-1;
294                                 inc=-1;
295                         }
296                         for (i = 0; i < height; i++) {
297                                 BEGIN_RING(NvImageBlit,
298                                            NV_IMAGE_BLIT_POINT_IN, 3);
299                                 OUT_RING  (((srcY+ypos) << 16) | srcX);
300                                 OUT_RING  (((dstY+ypos) << 16) | dstX);
301                                 OUT_RING  ((1  << 16) | width);
302                                 ypos+=inc;
303                         }
304                 } 
305         } else {
306                 NVDEBUG("ExaCopy: Using default path\n");
307                 BEGIN_RING(NvImageBlit, NV_IMAGE_BLIT_POINT_IN, 3);
308                 OUT_RING  ((srcY << 16) | srcX);
309                 OUT_RING  ((dstY << 16) | dstX);
310                 OUT_RING  ((height  << 16) | width);
311         }
312 #endif /* 0 */
313
314         NVDEBUG("ExaCopy: Using default path\n");
315         BEGIN_RING(NvImageBlit, NV01_IMAGE_BLIT_POINT_IN, 3);
316         OUT_RING  ((srcY << 16) | srcX);
317         OUT_RING  ((dstY << 16) | dstX);
318         OUT_RING  ((height  << 16) | width);
319
320         if((width * height) >= 512)
321                 FIRE_RING(); 
322 }
323
324 static void NVExaDoneCopy (PixmapPtr pDstPixmap) {}
325
326 static inline Bool NVAccelMemcpyRect(char *dst, const char *src, int height,
327                        int dst_pitch, int src_pitch, int line_len)
328 {
329         if ((src_pitch == line_len) && (src_pitch == dst_pitch)) {
330                 memcpy(dst, src, line_len*height);
331         } else {
332                 while (height--) {
333                         memcpy(dst, src, line_len);
334                         src += src_pitch;
335                         dst += dst_pitch;
336                 }
337         }
338
339         return TRUE;
340 }
341
342 static inline Bool
343 NVAccelDownloadM2MF(ScrnInfoPtr pScrn, char *dst, PixmapPtr pspix,
344                     uint32_t src_offset, int dst_pitch, int src_pitch,
345                     int line_len, int line_count)
346 {
347         NVPtr pNv = NVPTR(pScrn);
348
349         BEGIN_RING(NvMemFormat, 0x184, 2);
350         OUT_PIXMAPo(pspix, NOUVEAU_BO_GART | NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
351         OUT_RELOCo(pNv->GART, NOUVEAU_BO_GART | NOUVEAU_BO_WR);
352
353         while (line_count) {
354                 char *src = pNv->GART->map;
355                 int lc, i;
356
357                 if (line_count * line_len <= pNv->GART->size) {
358                         lc = line_count;
359                 } else {
360                         lc = pNv->GART->size / line_len;
361                         if (lc > line_count)
362                                 lc = line_count;
363                 }
364
365                 /* HW limitations */
366                 if (lc > 2047)
367                         lc = 2047;
368
369                 if (pNv->Architecture >= NV_ARCH_50) {
370                         BEGIN_RING(NvMemFormat, 0x200, 1);
371                         OUT_RING  (1);
372                         BEGIN_RING(NvMemFormat, 0x21c, 1);
373                         OUT_RING  (1);
374                         /* probably high-order bits of address */
375                         BEGIN_RING(NvMemFormat, 0x238, 2);
376                         OUT_PIXMAPh(pspix, src_offset, NOUVEAU_BO_GART |
377                                     NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
378                         OUT_RELOCh(pNv->GART, 0, NOUVEAU_BO_GART |
379                                    NOUVEAU_BO_WR);
380                 }
381
382                 BEGIN_RING(NvMemFormat,
383                            NV04_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
384                 OUT_PIXMAPl(pspix, src_offset, NOUVEAU_BO_GART |
385                             NOUVEAU_BO_VRAM | NOUVEAU_BO_RD);
386                 OUT_RELOCl(pNv->GART, 0, NOUVEAU_BO_GART | NOUVEAU_BO_WR);
387                 OUT_RING  (src_pitch);
388                 OUT_RING  (line_len);
389                 OUT_RING  (line_len);
390                 OUT_RING  (lc);
391                 OUT_RING  ((1<<8)|1);
392                 OUT_RING  (0);
393
394                 nouveau_notifier_reset(pNv->notify0, 0);
395                 BEGIN_RING(NvMemFormat, NV04_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
396                 OUT_RING  (0);
397                 BEGIN_RING(NvMemFormat, 0x100, 1);
398                 OUT_RING  (0);
399                 FIRE_RING();
400                 if (nouveau_notifier_wait_status(pNv->notify0, 0, 0,
401                                                  2000))
402                         return FALSE;
403
404                 if (dst_pitch == line_len) {
405                         memcpy(dst, src, dst_pitch * lc);
406                         dst += dst_pitch * lc;
407                 } else {
408                         for (i = 0; i < lc; i++) {
409                                 memcpy(dst, src, line_len);
410                                 src += line_len;
411                                 dst += dst_pitch;
412                         }
413                 }
414
415                 line_count -= lc;
416                 src_offset += lc * src_pitch;
417         }
418
419         return TRUE;
420 }
421
422 static inline void *
423 NVExaPixmapMap(PixmapPtr pPix)
424 {
425         void *map;
426 #if NOUVEAU_EXA_PIXMAPS
427         struct nouveau_pixmap *nvpix;
428
429         nvpix = exaGetPixmapDriverPrivate(pPix);
430         if (!nvpix || !nvpix->bo)
431                 return NULL;
432
433         map = nvpix->bo->map;
434 #else
435         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
436         NVPtr pNv = NVPTR(pScrn);
437         map = pNv->FB->map + exaGetPixmapOffset(pPix);
438 #endif /* NOUVEAU_EXA_PIXMAPS */
439         return map;
440 }
441
442 static Bool NVDownloadFromScreen(PixmapPtr pSrc,
443                                  int x,  int y,
444                                  int w,  int h,
445                                  char *dst,  int dst_pitch)
446 {
447         ScrnInfoPtr pScrn = xf86Screens[pSrc->drawable.pScreen->myNum];
448         NVPtr pNv = NVPTR(pScrn);
449         int src_pitch, cpp, offset;
450         const char *src;
451
452         src_pitch  = exaGetPixmapPitch(pSrc);
453         cpp = pSrc->drawable.bitsPerPixel >> 3;
454         offset = (y * src_pitch) + (x * cpp);
455
456         if (pNv->GART) {
457                 if (NVAccelDownloadM2MF(pScrn, dst, pSrc, offset,
458                                         dst_pitch, src_pitch, w * cpp, h))
459                         return TRUE;
460         }
461
462         src = NVExaPixmapMap(pSrc);
463         if (!src)
464                 return FALSE;
465         src += offset;
466         exaWaitSync(pSrc->drawable.pScreen);
467         if (NVAccelMemcpyRect(dst, src, h, dst_pitch, src_pitch, w*cpp))
468                 return TRUE;
469
470         return FALSE;
471 }
472
473 static inline Bool
474 NVAccelUploadIFC(ScrnInfoPtr pScrn, const char *src, int src_pitch, 
475                  PixmapPtr pDst, int x, int y, int w, int h, int cpp)
476 {
477         NVPtr pNv = NVPTR(pScrn);
478         int line_len = w * cpp;
479         int iw, id, surf_fmt, ifc_fmt;
480
481         if (pNv->Architecture >= NV_ARCH_50)
482                 return FALSE;
483
484         if (h > 1024)
485                 return FALSE;
486
487         switch (cpp) {
488         case 2: ifc_fmt = 1; break;
489         case 4: ifc_fmt = 4; break;
490         default:
491                 return FALSE;
492         }
493
494         if (!NVAccelGetCtxSurf2DFormatFromPixmap(pDst, &surf_fmt))
495                 return FALSE;
496
497         BEGIN_RING(NvContextSurfaces, NV04_CONTEXT_SURFACES_2D_FORMAT, 4);
498         OUT_RING  (surf_fmt);
499         OUT_RING  ((exaGetPixmapPitch(pDst) << 16) | exaGetPixmapPitch(pDst));
500         OUT_PIXMAPl(pDst, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
501         OUT_PIXMAPl(pDst, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
502
503         /* Pad out input width to cover both COLORA() and COLORB() */
504         iw  = (line_len + 7) & ~7;
505         id  = iw / 4; /* line push size */
506         iw /= cpp;
507
508         /* Don't support lines longer than max push size yet.. */
509         if (id > 1792)
510                 return FALSE;
511
512         BEGIN_RING(NvClipRectangle, NV01_CONTEXT_CLIP_RECTANGLE_POINT, 2);
513         OUT_RING  (0x0); 
514         OUT_RING  (0x7FFF7FFF);
515
516         BEGIN_RING(NvImageFromCpu, NV01_IMAGE_FROM_CPU_OPERATION, 2);
517         OUT_RING  (NV01_IMAGE_FROM_CPU_OPERATION_SRCCOPY);
518         OUT_RING  (ifc_fmt);
519         BEGIN_RING(NvImageFromCpu, NV01_IMAGE_FROM_CPU_POINT, 3);
520         OUT_RING  ((y << 16) | x); /* dst point */
521         OUT_RING  ((h << 16) | w); /* width/height out */
522         OUT_RING  ((h << 16) | iw); /* width/height in */
523
524         while (h--) {
525                 /* send a line */
526                 BEGIN_RING(NvImageFromCpu, NV01_IMAGE_FROM_CPU_COLOR(0), id);
527                 OUT_RINGp (src, id);
528
529                 src += src_pitch;
530         }
531
532         return TRUE;
533 }
534
535 static inline Bool
536 NVAccelUploadM2MF(ScrnInfoPtr pScrn, PixmapPtr pdpix, uint32_t dst_offset,
537                   const char *src, int dst_pitch, int src_pitch,
538                   int line_len, int line_count)
539 {
540         NVPtr pNv = NVPTR(pScrn);
541
542         BEGIN_RING(NvMemFormat, 0x184, 2);
543         OUT_RELOCo(pNv->GART, NOUVEAU_BO_GART | NOUVEAU_BO_RD);
544         OUT_PIXMAPo(pdpix, NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_WR);
545
546         while (line_count) {
547                 char *dst = pNv->GART->map;
548                 int lc, i;
549
550                 /* Determine max amount of data we can DMA at once */
551                 if (line_count * line_len <= pNv->GART->size) {
552                         lc = line_count;
553                 } else {
554                         lc = pNv->GART->size / line_len;
555                         if (lc > line_count)
556                                 lc = line_count;
557                 }
558
559                 /* HW limitations */
560                 if (lc > 2047)
561                         lc = 2047;
562
563                 /* Upload to GART */
564                 if (src_pitch == line_len) {
565                         memcpy(dst, src, src_pitch * lc);
566                         src += src_pitch * lc;
567                 } else {
568                         for (i = 0; i < lc; i++) {
569                                 memcpy(dst, src, line_len);
570                                 src += src_pitch;
571                                 dst += line_len;
572                         }
573                 }
574
575                 if (pNv->Architecture >= NV_ARCH_50) {
576                         BEGIN_RING(NvMemFormat, 0x200, 1);
577                         OUT_RING  (1);
578                         BEGIN_RING(NvMemFormat, 0x21c, 1);
579                         OUT_RING  (1);
580                         /* probably high-order bits of address */
581                         BEGIN_RING(NvMemFormat, 0x238, 2);
582                         OUT_RELOCh(pNv->GART, 0, NOUVEAU_BO_GART |
583                                    NOUVEAU_BO_RD);
584                         OUT_PIXMAPh(pdpix, 0, NOUVEAU_BO_VRAM | 
585                                     NOUVEAU_BO_GART | NOUVEAU_BO_WR);
586                 }
587
588                 /* DMA to VRAM */
589                 BEGIN_RING(NvMemFormat,
590                            NV04_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
591                 OUT_RELOCl(pNv->GART, 0, NOUVEAU_BO_GART | NOUVEAU_BO_RD);
592                 OUT_PIXMAPl(pdpix, dst_offset, NOUVEAU_BO_VRAM |
593                             NOUVEAU_BO_GART | NOUVEAU_BO_WR);
594                 OUT_RING  (line_len);
595                 OUT_RING  (dst_pitch);
596                 OUT_RING  (line_len);
597                 OUT_RING  (lc);
598                 OUT_RING  ((1<<8)|1);
599                 OUT_RING  (0);
600
601                 nouveau_notifier_reset(pNv->notify0, 0);
602                 BEGIN_RING(NvMemFormat, NV04_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
603                 OUT_RING  (0);
604                 BEGIN_RING(NvMemFormat, 0x100, 1);
605                 OUT_RING  (0);
606                 FIRE_RING();
607                 if (nouveau_notifier_wait_status(pNv->notify0, 0, 0, 2000))
608                         return FALSE;
609
610                 dst_offset += lc * dst_pitch;
611                 line_count -= lc;
612         }
613
614         return TRUE;
615 }
616
617 static Bool NVUploadToScreen(PixmapPtr pDst,
618                              int x, int y, int w, int h,
619                              char *src, int src_pitch)
620 {
621         ScrnInfoPtr pScrn = xf86Screens[pDst->drawable.pScreen->myNum];
622         NVPtr pNv = NVPTR(pScrn);
623         int dst_pitch, cpp;
624         char *dst;
625
626         dst_pitch  = exaGetPixmapPitch(pDst);
627         cpp = pDst->drawable.bitsPerPixel >> 3;
628
629         /* try hostdata transfer */
630         if (w * h * cpp < 16*1024) /* heuristic */
631         {
632                 if (pNv->Architecture < NV_ARCH_50) {
633                         if (NVAccelUploadIFC(pScrn, src, src_pitch, pDst,
634                                              x, y, w, h, cpp)) {
635                                 exaMarkSync(pDst->drawable.pScreen);
636                                 return TRUE;
637                         }
638                 } else {
639                         if (NV50EXAUploadSIFC(pScrn, src, src_pitch, pDst,
640                                               x, y, w, h, cpp)) {
641                                 exaMarkSync(pDst->drawable.pScreen);
642                                 return TRUE;
643                         }
644                 }
645         }
646
647         /* try gart-based transfer */
648         if (pNv->GART) {
649                 if (NVAccelUploadM2MF(pScrn, pDst, (y * dst_pitch) + (x * cpp),
650                                       src, dst_pitch, src_pitch, w * cpp, h))
651                         return TRUE;
652         }
653
654         /* fallback to memcpy-based transfer */
655         dst = NVExaPixmapMap(pDst);
656         if (!dst)
657                 return FALSE;
658         dst += (y * dst_pitch) + (x * cpp);
659         exaWaitSync(pDst->drawable.pScreen);
660         if (NVAccelMemcpyRect(dst, src, h, dst_pitch, src_pitch, w*cpp))
661                 return TRUE;
662
663         return FALSE;
664 }
665
666 #ifdef NOUVEAU_EXA_PIXMAPS
667 static Bool
668 NVExaPrepareAccess(PixmapPtr pPix, int index)
669 {
670         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
671         NVPtr pNv = NVPTR(pScrn);
672         struct nouveau_pixmap *nvpix;
673         (void)pNv;
674
675         nvpix = exaGetPixmapDriverPrivate(pPix);
676         if (!nvpix || !nvpix->bo)
677                 return FALSE;
678
679         /*XXX: ho hum.. sync if needed */
680
681         if (nvpix->mapped)
682                 return TRUE;
683
684         if (nouveau_bo_map(nvpix->bo, NOUVEAU_BO_RDWR))
685                 return FALSE;
686         pPix->devPrivate.ptr = nvpix->bo->map;
687         nvpix->mapped = TRUE;
688         return TRUE;
689 }
690
691 static void
692 NVExaFinishAccess(PixmapPtr pPix, int index)
693 {
694         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
695         NVPtr pNv = NVPTR(pScrn);
696         struct nouveau_pixmap *nvpix;
697         (void)pNv;
698
699         nvpix = exaGetPixmapDriverPrivate(pPix);
700         if (!nvpix || !nvpix->bo || !nvpix->mapped)
701                 return;
702
703         nouveau_bo_unmap(nvpix->bo);
704         pPix->devPrivate.ptr = NULL;
705         nvpix->mapped = FALSE;
706 }
707
708 static Bool
709 NVExaPixmapIsOffscreen(PixmapPtr pPix)
710 {
711         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
712         NVPtr pNv = NVPTR(pScrn);
713         struct nouveau_pixmap *nvpix;
714         (void)pNv;
715
716         nvpix = exaGetPixmapDriverPrivate(pPix);
717         if (!nvpix || !nvpix->bo)
718                 return FALSE;
719
720         return TRUE;
721 }
722
723 static void *
724 NVExaCreatePixmap(ScreenPtr pScreen, int size, int align)
725 {
726         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
727         NVPtr pNv = NVPTR(pScrn);
728         struct nouveau_pixmap *nvpix;
729
730         nvpix = xcalloc(1, sizeof(struct nouveau_pixmap));
731         if (!nvpix)
732                 return NULL;
733
734         if (size) {
735                 if (nouveau_bo_new(pNv->dev, NOUVEAU_BO_VRAM, 0, size,
736                                    &nvpix->bo)) {
737                         xfree(nvpix);
738                         return NULL;
739                 }
740         }
741
742         return nvpix;
743 }
744
745 static void
746 NVExaDestroyPixmap(ScreenPtr pScreen, void *driverPriv)
747 {
748         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
749         NVPtr pNv = NVPTR(pScrn);
750         struct nouveau_pixmap *nvpix = driverPriv;
751
752         if (!driverPriv)
753                 return;
754
755         /*XXX: only if pending relocs reference this buffer..*/
756         FIRE_RING();
757
758         nouveau_bo_del(&nvpix->bo);
759         xfree(nvpix);
760 }
761
762 static Bool
763 NVExaModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, int depth,
764                         int bitsPerPixel, int devKind, pointer pPixData)
765 {
766         ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
767         NVPtr pNv = NVPTR(pScrn);
768         struct nouveau_pixmap *nvpix;
769
770         if (pPixData == pNv->FB->map) {
771                 nvpix = exaGetPixmapDriverPrivate(pPixmap);
772                 if (!nvpix)
773                         return FALSE;
774
775                 if (nouveau_bo_ref(pNv->dev, pNv->FB->handle, &nvpix->bo))
776                         return FALSE;
777
778                 miModifyPixmapHeader(pPixmap, width, height, depth,
779                                      bitsPerPixel, devKind, NULL);
780                 return TRUE;
781         }
782
783         return FALSE;
784 }
785 #endif
786
787 Bool
788 NVExaPixmapIsOnscreen(PixmapPtr pPixmap)
789 {
790         ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
791         NVPtr pNv = NVPTR(pScrn);
792
793 #if NOUVEAU_EXA_PIXMAPS
794         struct nouveau_pixmap *nvpix;
795         nvpix = exaGetPixmapDriverPrivate(pPixmap);
796
797         if (nvpix && nvpix->bo == pNv->FB)
798                 return TRUE;
799
800 #else
801         unsigned long offset = exaGetPixmapOffset(pPixmap);
802
803         if (offset < pNv->EXADriverPtr->offScreenBase)
804                 return TRUE;
805 #endif /* NOUVEAU_EXA_PIXMAPS */
806
807         return FALSE;
808 }
809
810 Bool
811 NVExaInit(ScreenPtr pScreen) 
812 {
813         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
814         NVPtr pNv = NVPTR(pScrn);
815
816         if(!(pNv->EXADriverPtr = (ExaDriverPtr) xnfcalloc(sizeof(ExaDriverRec), 1))) {
817                 pNv->NoAccel = TRUE;
818                 return FALSE;
819         }
820
821         pNv->EXADriverPtr->exa_major = EXA_VERSION_MAJOR;
822         pNv->EXADriverPtr->exa_minor = EXA_VERSION_MINOR;
823
824 #ifdef NOUVEAU_EXA_PIXMAPS
825         if (NOUVEAU_EXA_PIXMAPS) {
826                 pNv->EXADriverPtr->flags = EXA_OFFSCREEN_PIXMAPS |
827                                            EXA_HANDLES_PIXMAPS;
828                 pNv->EXADriverPtr->PrepareAccess = NVExaPrepareAccess;
829                 pNv->EXADriverPtr->FinishAccess = NVExaFinishAccess;
830                 pNv->EXADriverPtr->PixmapIsOffscreen = NVExaPixmapIsOffscreen;
831                 pNv->EXADriverPtr->CreatePixmap = NVExaCreatePixmap;
832                 pNv->EXADriverPtr->DestroyPixmap = NVExaDestroyPixmap;
833                 pNv->EXADriverPtr->ModifyPixmapHeader = NVExaModifyPixmapHeader;
834         } else
835 #endif
836         {
837                 pNv->EXADriverPtr->flags = EXA_OFFSCREEN_PIXMAPS;
838                 pNv->EXADriverPtr->memoryBase = pNv->FB->map;
839                 pNv->EXADriverPtr->offScreenBase =
840                         NOUVEAU_ALIGN(pScrn->virtualX, 64) * NOUVEAU_ALIGN(pScrn->virtualY,64) * 
841                         (pScrn->bitsPerPixel / 8); 
842                 pNv->EXADriverPtr->memorySize           = pNv->FB->size; 
843         }
844         pNv->EXADriverPtr->pixmapOffsetAlign    = 256; 
845         pNv->EXADriverPtr->pixmapPitchAlign     = 64; 
846
847         if (pNv->Architecture >= NV_ARCH_50) {
848                 pNv->EXADriverPtr->maxX = 8192;
849                 pNv->EXADriverPtr->maxY = 8192;
850         } else
851         if (pNv->Architecture >= NV_ARCH_20) {
852                 pNv->EXADriverPtr->maxX = 4096;
853                 pNv->EXADriverPtr->maxY = 4096;
854         } else {
855                 pNv->EXADriverPtr->maxX = 2048;
856                 pNv->EXADriverPtr->maxY = 2048;
857         }
858
859         pNv->EXADriverPtr->WaitMarker = NVExaWaitMarker;
860
861         /* Install default hooks */
862         pNv->EXADriverPtr->DownloadFromScreen = NVDownloadFromScreen; 
863         pNv->EXADriverPtr->UploadToScreen = NVUploadToScreen; 
864
865         if (pNv->Architecture < NV_ARCH_50) {
866                 pNv->EXADriverPtr->PrepareCopy = NVExaPrepareCopy;
867                 pNv->EXADriverPtr->Copy = NVExaCopy;
868                 pNv->EXADriverPtr->DoneCopy = NVExaDoneCopy;
869
870                 pNv->EXADriverPtr->PrepareSolid = NVExaPrepareSolid;
871                 pNv->EXADriverPtr->Solid = NVExaSolid;
872                 pNv->EXADriverPtr->DoneSolid = NVExaDoneSolid;
873         } else {
874                 pNv->EXADriverPtr->PrepareCopy = NV50EXAPrepareCopy;
875                 pNv->EXADriverPtr->Copy = NV50EXACopy;
876                 pNv->EXADriverPtr->DoneCopy = NV50EXADoneCopy;
877
878                 pNv->EXADriverPtr->PrepareSolid = NV50EXAPrepareSolid;
879                 pNv->EXADriverPtr->Solid = NV50EXASolid;
880                 pNv->EXADriverPtr->DoneSolid = NV50EXADoneSolid;
881         }
882
883         switch (pNv->Architecture) {
884         
885         case NV_ARCH_10:
886         case NV_ARCH_20:
887                 pNv->EXADriverPtr->CheckComposite   = NV10CheckComposite;
888                 pNv->EXADriverPtr->PrepareComposite = NV10PrepareComposite;
889                 pNv->EXADriverPtr->Composite        = NV10Composite;
890                 pNv->EXADriverPtr->DoneComposite    = NV10DoneComposite;
891                 break;
892         case NV_ARCH_30:
893                 pNv->EXADriverPtr->CheckComposite   = NV30EXACheckComposite;
894                 pNv->EXADriverPtr->PrepareComposite = NV30EXAPrepareComposite;
895                 pNv->EXADriverPtr->Composite        = NV30EXAComposite;
896                 pNv->EXADriverPtr->DoneComposite    = NV30EXADoneComposite;
897                 break;
898         case NV_ARCH_40:
899                 pNv->EXADriverPtr->CheckComposite   = NV40EXACheckComposite;
900                 pNv->EXADriverPtr->PrepareComposite = NV40EXAPrepareComposite;
901                 pNv->EXADriverPtr->Composite        = NV40EXAComposite;
902                 pNv->EXADriverPtr->DoneComposite    = NV40EXADoneComposite;
903                 break;
904         case NV_ARCH_50:
905                 break;
906         default:
907                 break;
908         }
909
910         if (!exaDriverInit(pScreen, pNv->EXADriverPtr))
911                 return FALSE;
912         else
913                 /* EXA init catches this, but only for xserver >= 1.4 */
914                 if (pNv->VRAMPhysicalSize / 2 < NOUVEAU_ALIGN(pScrn->virtualX, 64) * NOUVEAU_ALIGN(pScrn->virtualY, 64) * (pScrn->bitsPerPixel >> 3)) {
915                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "The virtual screen size's resolution is too big for the video RAM framebuffer at this colour depth.\n");
916                         return FALSE;
917                 }
918
919         return TRUE;
920 }