randr12: Add some extra safeties (to mode checking).
[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, NV_IMAGE_BLIT_OPERATION, 1);
216                 OUT_RING  (1); /* ROP_AND */
217                 NVSetROP(pScrn, alu, planemask);
218         } else {
219                 BEGIN_RING(NvImageBlit, NV_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, NV_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                            NV_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, NV_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         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
426         NVPtr pNv = NVPTR(pScrn);
427         void *map;
428
429         map = pNv->FB->map + exaGetPixmapOffset(pPix);
430         return map;
431 }
432
433 static Bool NVDownloadFromScreen(PixmapPtr pSrc,
434                                  int x,  int y,
435                                  int w,  int h,
436                                  char *dst,  int dst_pitch)
437 {
438         ScrnInfoPtr pScrn = xf86Screens[pSrc->drawable.pScreen->myNum];
439         NVPtr pNv = NVPTR(pScrn);
440         int src_pitch, cpp, offset;
441         const char *src;
442
443         src_pitch  = exaGetPixmapPitch(pSrc);
444         cpp = pSrc->drawable.bitsPerPixel >> 3;
445         offset = (y * src_pitch) + (x * cpp);
446
447         if (pNv->GART) {
448                 if (NVAccelDownloadM2MF(pScrn, dst, pSrc, offset,
449                                         dst_pitch, src_pitch, w * cpp, h))
450                         return TRUE;
451         }
452
453         src = NVExaPixmapMap(pSrc) + offset;
454         exaWaitSync(pSrc->drawable.pScreen);
455         if (NVAccelMemcpyRect(dst, src, h, dst_pitch, src_pitch, w*cpp))
456                 return TRUE;
457
458         return FALSE;
459 }
460
461 static inline Bool
462 NVAccelUploadIFC(ScrnInfoPtr pScrn, const char *src, int src_pitch, 
463                  PixmapPtr pDst, int x, int y, int w, int h, int cpp)
464 {
465         NVPtr pNv = NVPTR(pScrn);
466         int line_len = w * cpp;
467         int iw, id, surf_fmt, ifc_fmt;
468
469         if (pNv->Architecture >= NV_ARCH_50)
470                 return FALSE;
471
472         if (h > 1024)
473                 return FALSE;
474
475         switch (cpp) {
476         case 2: ifc_fmt = 1; break;
477         case 4: ifc_fmt = 4; break;
478         default:
479                 return FALSE;
480         }
481
482         if (!NVAccelGetCtxSurf2DFormatFromPixmap(pDst, &surf_fmt))
483                 return FALSE;
484
485         BEGIN_RING(NvContextSurfaces, NV04_CONTEXT_SURFACES_2D_FORMAT, 4);
486         OUT_RING  (surf_fmt);
487         OUT_RING  ((exaGetPixmapPitch(pDst) << 16) | exaGetPixmapPitch(pDst));
488         OUT_PIXMAPl(pDst, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
489         OUT_PIXMAPl(pDst, 0, NOUVEAU_BO_VRAM | NOUVEAU_BO_WR);
490
491         /* Pad out input width to cover both COLORA() and COLORB() */
492         iw  = (line_len + 7) & ~7;
493         id  = iw / 4; /* line push size */
494         iw /= cpp;
495
496         /* Don't support lines longer than max push size yet.. */
497         if (id > 1792)
498                 return FALSE;
499
500         BEGIN_RING(NvClipRectangle, NV01_CONTEXT_CLIP_RECTANGLE_POINT, 2);
501         OUT_RING  (0x0); 
502         OUT_RING  (0x7FFF7FFF);
503
504         BEGIN_RING(NvImageFromCpu, NV01_IMAGE_FROM_CPU_OPERATION, 2);
505         OUT_RING  (NV01_IMAGE_FROM_CPU_OPERATION_SRCCOPY);
506         OUT_RING  (ifc_fmt);
507         BEGIN_RING(NvImageFromCpu, NV01_IMAGE_FROM_CPU_POINT, 3);
508         OUT_RING  ((y << 16) | x); /* dst point */
509         OUT_RING  ((h << 16) | w); /* width/height out */
510         OUT_RING  ((h << 16) | iw); /* width/height in */
511
512         while (h--) {
513                 /* send a line */
514                 BEGIN_RING(NvImageFromCpu, NV01_IMAGE_FROM_CPU_COLOR(0), id);
515                 OUT_RINGp (src, id);
516
517                 src += src_pitch;
518         }
519
520         return TRUE;
521 }
522
523 static inline Bool
524 NVAccelUploadM2MF(ScrnInfoPtr pScrn, PixmapPtr pdpix, uint32_t dst_offset,
525                   const char *src, int dst_pitch, int src_pitch,
526                   int line_len, int line_count)
527 {
528         NVPtr pNv = NVPTR(pScrn);
529
530         BEGIN_RING(NvMemFormat, 0x184, 2);
531         OUT_RELOCo(pNv->GART, NOUVEAU_BO_GART | NOUVEAU_BO_RD);
532         OUT_PIXMAPo(pdpix, NOUVEAU_BO_VRAM | NOUVEAU_BO_GART | NOUVEAU_BO_WR);
533
534         while (line_count) {
535                 char *dst = pNv->GART->map;
536                 int lc, i;
537
538                 /* Determine max amount of data we can DMA at once */
539                 if (line_count * line_len <= pNv->GART->size) {
540                         lc = line_count;
541                 } else {
542                         lc = pNv->GART->size / line_len;
543                         if (lc > line_count)
544                                 lc = line_count;
545                 }
546
547                 /* HW limitations */
548                 if (lc > 2047)
549                         lc = 2047;
550
551                 /* Upload to GART */
552                 if (src_pitch == line_len) {
553                         memcpy(dst, src, src_pitch * lc);
554                         src += src_pitch * lc;
555                 } else {
556                         for (i = 0; i < lc; i++) {
557                                 memcpy(dst, src, line_len);
558                                 src += src_pitch;
559                                 dst += line_len;
560                         }
561                 }
562
563                 if (pNv->Architecture >= NV_ARCH_50) {
564                         BEGIN_RING(NvMemFormat, 0x200, 1);
565                         OUT_RING  (1);
566                         BEGIN_RING(NvMemFormat, 0x21c, 1);
567                         OUT_RING  (1);
568                         /* probably high-order bits of address */
569                         BEGIN_RING(NvMemFormat, 0x238, 2);
570                         OUT_RELOCh(pNv->GART, 0, NOUVEAU_BO_GART |
571                                    NOUVEAU_BO_RD);
572                         OUT_PIXMAPh(pdpix, 0, NOUVEAU_BO_VRAM | 
573                                     NOUVEAU_BO_GART | NOUVEAU_BO_WR);
574                 }
575
576                 /* DMA to VRAM */
577                 BEGIN_RING(NvMemFormat,
578                            NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
579                 OUT_RELOCl(pNv->GART, 0, NOUVEAU_BO_GART | NOUVEAU_BO_RD);
580                 OUT_PIXMAPl(pdpix, dst_offset, NOUVEAU_BO_VRAM |
581                             NOUVEAU_BO_GART | NOUVEAU_BO_WR);
582                 OUT_RING  (line_len);
583                 OUT_RING  (dst_pitch);
584                 OUT_RING  (line_len);
585                 OUT_RING  (lc);
586                 OUT_RING  ((1<<8)|1);
587                 OUT_RING  (0);
588
589                 nouveau_notifier_reset(pNv->notify0, 0);
590                 BEGIN_RING(NvMemFormat, NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1);
591                 OUT_RING  (0);
592                 BEGIN_RING(NvMemFormat, 0x100, 1);
593                 OUT_RING  (0);
594                 FIRE_RING();
595                 if (nouveau_notifier_wait_status(pNv->notify0, 0, 0, 2000))
596                         return FALSE;
597
598                 dst_offset += lc * dst_pitch;
599                 line_count -= lc;
600         }
601
602         return TRUE;
603 }
604
605 static Bool NVUploadToScreen(PixmapPtr pDst,
606                              int x, int y, int w, int h,
607                              char *src, int src_pitch)
608 {
609         ScrnInfoPtr pScrn = xf86Screens[pDst->drawable.pScreen->myNum];
610         NVPtr pNv = NVPTR(pScrn);
611         int dst_pitch, cpp;
612         char *dst;
613
614         dst_pitch  = exaGetPixmapPitch(pDst);
615         cpp = pDst->drawable.bitsPerPixel >> 3;
616
617         /* try hostdata transfer */
618         if (pNv->Architecture < NV_ARCH_50 && w*h*cpp<16*1024) /* heuristic */
619         {
620                 if (NVAccelUploadIFC(pScrn, src, src_pitch, pDst,
621                                      x, y, w, h, cpp)) {
622                         exaMarkSync(pDst->drawable.pScreen);
623                         return TRUE;
624                 }
625         }
626
627         /* try gart-based transfer */
628         if (pNv->GART) {
629                 if (NVAccelUploadM2MF(pScrn, pDst, (y * dst_pitch) + (x * cpp),
630                                       src, dst_pitch, src_pitch, w * cpp, h))
631                         return TRUE;
632         }
633
634         /* fallback to memcpy-based transfer */
635         dst = NVExaPixmapMap(pDst) + (y * dst_pitch) + (x * cpp);
636         exaWaitSync(pDst->drawable.pScreen);
637         if (NVAccelMemcpyRect(dst, src, h, dst_pitch, src_pitch, w*cpp))
638                 return TRUE;
639
640         return FALSE;
641 }
642
643 #ifdef NOUVEAU_EXA_PIXMAPS
644 static Bool
645 NVExaPrepareAccess(PixmapPtr pPix, int index)
646 {
647         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
648         NVPtr pNv = NVPTR(pScrn);
649         struct nouveau_pixmap *nvpix;
650         (void)pNv;
651
652         nvpix = exaGetPixmapDriverPrivate(pPix);
653         if (!nvpix || !nvpix->bo)
654                 return FALSE;
655
656         /*XXX: ho hum.. sync if needed */
657
658         if (nvpix->mapped)
659                 return TRUE;
660
661         if (nouveau_bo_map(nvpix->bo, NOUVEAU_BO_RDWR))
662                 return FALSE;
663         pPix->devPrivate.ptr = nvpix->bo->map;
664         nvpix->mapped = TRUE;
665         return TRUE;
666 }
667
668 static void
669 NVExaFinishAccess(PixmapPtr pPix, int index)
670 {
671         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
672         NVPtr pNv = NVPTR(pScrn);
673         struct nouveau_pixmap *nvpix;
674         (void)pNv;
675
676         nvpix = exaGetPixmapDriverPrivate(pPix);
677         if (!nvpix || !nvpix->bo || !nvpix->mapped)
678                 return;
679
680         nouveau_bo_unmap(nvpix->bo);
681         pPix->devPrivate.ptr = NULL;
682         nvpix->mapped = FALSE;
683 }
684
685 static Bool
686 NVExaPixmapIsOffscreen(PixmapPtr pPix)
687 {
688         ScrnInfoPtr pScrn = xf86Screens[pPix->drawable.pScreen->myNum];
689         NVPtr pNv = NVPTR(pScrn);
690         struct nouveau_pixmap *nvpix;
691         (void)pNv;
692
693         nvpix = exaGetPixmapDriverPrivate(pPix);
694         if (!nvpix || !nvpix->bo)
695                 return FALSE;
696
697         return TRUE;
698 }
699
700 static void *
701 NVExaCreatePixmap(ScreenPtr pScreen, int size, int align)
702 {
703         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
704         NVPtr pNv = NVPTR(pScrn);
705         struct nouveau_pixmap *nvpix;
706
707         nvpix = xcalloc(1, sizeof(struct nouveau_pixmap));
708         if (!nvpix)
709                 return NULL;
710
711         if (size) {
712                 if (nouveau_bo_new(pNv->dev, NOUVEAU_BO_VRAM, 0, size,
713                                    &nvpix->bo)) {
714                         xfree(nvpix);
715                         return NULL;
716                 }
717         }
718
719         return nvpix;
720 }
721
722 static void
723 NVExaDestroyPixmap(ScreenPtr pScreen, void *driverPriv)
724 {
725         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
726         NVPtr pNv = NVPTR(pScrn);
727         struct nouveau_pixmap *nvpix = driverPriv;
728
729         if (!driverPriv)
730                 return;
731
732         /*XXX: only if pending relocs reference this buffer..*/
733         FIRE_RING();
734
735         nouveau_bo_del(&nvpix->bo);
736         xfree(nvpix);
737 }
738
739 static Bool
740 NVExaModifyPixmapHeader(PixmapPtr pPixmap, int width, int height, int depth,
741                         int bitsPerPixel, int devKind, pointer pPixData)
742 {
743         ScrnInfoPtr pScrn = xf86Screens[pPixmap->drawable.pScreen->myNum];
744         NVPtr pNv = NVPTR(pScrn);
745         struct nouveau_pixmap *nvpix;
746
747         if (pPixData == pNv->FB->map) {
748                 nvpix = exaGetPixmapDriverPrivate(pPixmap);
749                 if (!nvpix)
750                         return FALSE;
751
752                 if (nouveau_bo_ref(pNv->dev, pNv->FB->handle, &nvpix->bo))
753                         return FALSE;
754
755                 miModifyPixmapHeader(pPixmap, width, height, depth,
756                                      bitsPerPixel, devKind, NULL);
757                 return TRUE;
758         }
759
760         return FALSE;
761 }
762 #endif
763
764 Bool
765 NVExaInit(ScreenPtr pScreen) 
766 {
767         ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum];
768         NVPtr pNv = NVPTR(pScrn);
769
770         if(!(pNv->EXADriverPtr = (ExaDriverPtr) xnfcalloc(sizeof(ExaDriverRec), 1))) {
771                 pNv->NoAccel = TRUE;
772                 return FALSE;
773         }
774
775         pNv->EXADriverPtr->exa_major = EXA_VERSION_MAJOR;
776         pNv->EXADriverPtr->exa_minor = EXA_VERSION_MINOR;
777
778 #ifdef NOUVEAU_EXA_PIXMAPS
779         if (NOUVEAU_EXA_PIXMAPS) {
780                 pNv->EXADriverPtr->flags = EXA_OFFSCREEN_PIXMAPS |
781                                            EXA_HANDLES_PIXMAPS;
782                 pNv->EXADriverPtr->PrepareAccess = NVExaPrepareAccess;
783                 pNv->EXADriverPtr->FinishAccess = NVExaFinishAccess;
784                 pNv->EXADriverPtr->PixmapIsOffscreen = NVExaPixmapIsOffscreen;
785                 pNv->EXADriverPtr->CreatePixmap = NVExaCreatePixmap;
786                 pNv->EXADriverPtr->DestroyPixmap = NVExaDestroyPixmap;
787                 pNv->EXADriverPtr->ModifyPixmapHeader = NVExaModifyPixmapHeader;
788         } else
789 #endif
790         {
791                 pNv->EXADriverPtr->flags = EXA_OFFSCREEN_PIXMAPS;
792                 pNv->EXADriverPtr->memoryBase = pNv->FB->map;
793                 pNv->EXADriverPtr->offScreenBase =
794                         pScrn->virtualX * pScrn->virtualY * 
795                         (pScrn->bitsPerPixel / 8); 
796                 pNv->EXADriverPtr->memorySize           = pNv->FB->size; 
797         }
798         pNv->EXADriverPtr->pixmapOffsetAlign    = 256; 
799         pNv->EXADriverPtr->pixmapPitchAlign     = 64; 
800         pNv->EXADriverPtr->maxX                 = 32768;
801         pNv->EXADriverPtr->maxY                 = 32768;
802
803         pNv->EXADriverPtr->WaitMarker = NVExaWaitMarker;
804
805         /* Install default hooks */
806         pNv->EXADriverPtr->DownloadFromScreen = NVDownloadFromScreen; 
807         pNv->EXADriverPtr->UploadToScreen = NVUploadToScreen; 
808
809         if (pNv->Architecture < NV_ARCH_50) {
810                 pNv->EXADriverPtr->PrepareCopy = NVExaPrepareCopy;
811                 pNv->EXADriverPtr->Copy = NVExaCopy;
812                 pNv->EXADriverPtr->DoneCopy = NVExaDoneCopy;
813
814                 pNv->EXADriverPtr->PrepareSolid = NVExaPrepareSolid;
815                 pNv->EXADriverPtr->Solid = NVExaSolid;
816                 pNv->EXADriverPtr->DoneSolid = NVExaDoneSolid;
817         } else {
818                 pNv->EXADriverPtr->PrepareCopy = NV50EXAPrepareCopy;
819                 pNv->EXADriverPtr->Copy = NV50EXACopy;
820                 pNv->EXADriverPtr->DoneCopy = NV50EXADoneCopy;
821
822                 pNv->EXADriverPtr->PrepareSolid = NV50EXAPrepareSolid;
823                 pNv->EXADriverPtr->Solid = NV50EXASolid;
824                 pNv->EXADriverPtr->DoneSolid = NV50EXADoneSolid;
825         }
826
827         switch (pNv->Architecture) {
828         
829         case NV_ARCH_10:
830         case NV_ARCH_20:
831                 pNv->EXADriverPtr->CheckComposite   = NV10CheckComposite;
832                 pNv->EXADriverPtr->PrepareComposite = NV10PrepareComposite;
833                 pNv->EXADriverPtr->Composite        = NV10Composite;
834                 pNv->EXADriverPtr->DoneComposite    = NV10DoneComposite;
835                 break;
836         case NV_ARCH_30:
837                 pNv->EXADriverPtr->CheckComposite   = NV30EXACheckComposite;
838                 pNv->EXADriverPtr->PrepareComposite = NV30EXAPrepareComposite;
839                 pNv->EXADriverPtr->Composite        = NV30EXAComposite;
840                 pNv->EXADriverPtr->DoneComposite    = NV30EXADoneComposite;
841                 break;
842         case NV_ARCH_40:
843                 pNv->EXADriverPtr->CheckComposite   = NV40EXACheckComposite;
844                 pNv->EXADriverPtr->PrepareComposite = NV40EXAPrepareComposite;
845                 pNv->EXADriverPtr->Composite        = NV40EXAComposite;
846                 pNv->EXADriverPtr->DoneComposite    = NV40EXADoneComposite;
847                 break;
848         case NV_ARCH_50:
849                 break;
850         default:
851                 break;
852         }
853
854         return exaDriverInit(pScreen, pNv->EXADriverPtr);
855 }
856