I unknowingly broke CRT's i think.
[nouveau] / src / nv_dma.c
1 #include <errno.h>
2 #include "nv_include.h"
3 #include "nvreg.h"
4
5 void NVDmaKickoff(NVPtr pNv)
6 {
7         if(pNv->dmaCurrent != pNv->dmaPut) {
8                 pNv->dmaPut = pNv->dmaCurrent;
9                 WRITE_PUT(pNv,  pNv->dmaPut);
10         }
11 }
12
13 void NVDmaKickoffCallback(NVPtr pNv)
14 {
15         NVDmaKickoff(pNv);
16         pNv->DMAKickoffCallback = NULL;
17 }
18
19 static uint32_t subchannels[8];
20
21 void NVDmaStart(NVPtr pNv, uint32_t object, uint32_t tag, int size)
22 {
23         int subchannel=-1;
24         int i;
25         
26         /* XXX FIXME */
27         ScrnInfoPtr pScrn = xf86Screens[0];
28
29         /* look for a subchannel already bound to that object */
30         for(i=0;i<8;i++)
31         {
32                 if (subchannels[i]==object)
33                 {
34                         subchannel=i;
35                         break;
36                 }
37         }
38
39         /* add 2 for the potential subchannel binding */
40         if((pNv)->dmaFree <= (size + 2))
41                 NVDmaWait(pScrn, size + 2);
42
43         if (subchannel==-1)
44         {
45                 /* bind the object */
46                 subchannel=rand()%8;
47                 subchannels[subchannel]=object;
48                 NVDEBUG("Bind object %x on subchannel %d\n", (object), (subchannel));
49                 NVDmaNext(pNv, (1<<18) | (subchannel<<13));
50                 NVDmaNext(pNv,object);
51                 pNv->dmaFree -= (2);
52         }
53         NVDEBUG("NVDmaStart: subc=%d, cmd=%x, num=%d\n", (subchannel), (tag), (size));
54         NVDmaNext(pNv, ((size) << 18) | ((subchannel) << 13) | (tag));
55         pNv->dmaFree -= ((size) + 1); 
56 }
57
58
59 /* There is a HW race condition with videoram command buffers.
60  * You can't jump to the location of your put offset.  We write put
61  * at the jump offset + SKIPS dwords with noop padding in between
62  * to solve this problem
63  */
64 #define SKIPS  8
65
66 void NVDmaWait (ScrnInfoPtr pScrn, int size)
67 {
68         NVPtr pNv = NVPTR(pScrn);
69         int t_start;
70         int dmaGet;
71
72         size++;
73
74         t_start = GetTimeInMillis();
75         while(pNv->dmaFree < size) {
76                 dmaGet = READ_GET(pNv);
77
78                 if(pNv->dmaPut >= dmaGet) {
79                         pNv->dmaFree = pNv->dmaMax - pNv->dmaCurrent;
80                         if(pNv->dmaFree < size) {
81                                 NVDmaNext(pNv, (0x20000000|pNv->fifo.put_base));
82                                 if(dmaGet <= SKIPS) {
83                                         if(pNv->dmaPut <= SKIPS) /* corner case - will be idle */
84                                                 WRITE_PUT(pNv, SKIPS + 1);
85                                         do {
86                                                 if (GetTimeInMillis() - t_start > 2000)
87                                                         NVSync(pScrn);
88                                                 dmaGet = READ_GET(pNv);
89                                         } while(dmaGet <= SKIPS);
90                                 }
91                                 WRITE_PUT(pNv, SKIPS);
92                                 pNv->dmaCurrent = pNv->dmaPut = SKIPS;
93                                 pNv->dmaFree = dmaGet - (SKIPS + 1);
94                         }
95                 } else
96                         pNv->dmaFree = dmaGet - pNv->dmaCurrent - 1;
97
98                 if (GetTimeInMillis() - t_start > 2000)
99                         NVSync(pScrn);
100         }
101 }
102
103 static void NVDumpLockupInfo(NVPtr pNv)
104 {
105         int i,start;
106         start=READ_GET(pNv)-20;
107         if (start<0) start=0;
108         xf86DrvMsg(0, X_INFO, "Fifo dump (lockup 0x%04x,0x%04x):\n",READ_GET(pNv),pNv->dmaPut);
109         for(i=start;i<pNv->dmaPut+10;i++)
110                 xf86DrvMsg(0, X_INFO, "[0x%04x] 0x%08x\n", i, pNv->dmaBase[i]);
111         xf86DrvMsg(0, X_INFO, "End of fifo dump\n");
112 }
113
114 static void
115 NVLockedUp(ScrnInfoPtr pScrn)
116 {
117         NVPtr pNv = NVPTR(pScrn);
118
119         /* avoid re-entering FatalError on shutdown */
120         if (pNv->LockedUp)
121                 return;
122         pNv->LockedUp = TRUE;
123
124         NVDumpLockupInfo(pNv);
125
126         FatalError("DMA queue hang: dmaPut=%x, current=%x, status=%x\n",
127                    pNv->dmaPut, READ_GET(pNv), pNv->PGRAPH[NV_PGRAPH_STATUS/4]);
128 }
129
130 void NVSync(ScrnInfoPtr pScrn)
131 {
132         NVPtr pNv = NVPTR(pScrn);
133         int t_start, timeout = 2000;
134         int grobj = pNv->Architecture < NV_ARCH_50 ? NvImageBlit : Nv2D;
135
136         if(pNv->NoAccel)
137                 return;
138
139         if(pNv->DMAKickoffCallback)
140                 (*pNv->DMAKickoffCallback)(pNv);
141
142         /* Wait for entire FIFO to be processed */
143         t_start = GetTimeInMillis();
144         while((GetTimeInMillis() - t_start) < timeout &&
145                         (READ_GET(pNv) != pNv->dmaPut));
146         if ((GetTimeInMillis() - t_start) >= timeout) {
147                 NVLockedUp(pScrn);
148                 return;
149         }
150
151         /* Wait for channel to go completely idle */
152         NVNotifierReset(pScrn, pNv->Notifier0);
153         NVDmaStart(pNv, grobj, 0x104, 1);
154         NVDmaNext (pNv, 0);
155         NVDmaStart(pNv, grobj, 0x100, 1);
156         NVDmaNext (pNv, 0);
157         NVDmaKickoff(pNv);
158         if (!NVNotifierWaitStatus(pScrn, pNv->Notifier0, 0, timeout))
159                 NVLockedUp(pScrn);
160 }
161
162 void NVResetGraphics(ScrnInfoPtr pScrn)
163 {
164         NVPtr pNv = NVPTR(pScrn);
165         CARD32 surfaceFormat, patternFormat, rectFormat, lineFormat;
166         int pitch, i;
167
168         if(pNv->NoAccel) return;
169
170         pitch = pNv->CurrentLayout.displayWidth * (pNv->CurrentLayout.bitsPerPixel >> 3);
171
172         pNv->dmaPut = pNv->dmaCurrent = READ_GET(pNv);
173         pNv->dmaMax = (pNv->fifo.cmdbuf_size >> 2) - 2;
174         pNv->dmaFree = pNv->dmaMax - pNv->dmaCurrent;
175
176         /* assert there's enough room for the skips */
177         if(pNv->dmaFree <= SKIPS)
178                 NVDmaWait(pScrn, SKIPS); 
179         for (i=0; i<SKIPS; i++) {
180                 NVDmaNext(pNv,0);
181                 pNv->dmaBase[i]=0;
182         }
183         pNv->dmaFree -= SKIPS;
184
185         for(i=0;i<8;i++)
186                 subchannels[i]=0;
187
188         NVAccelCommonInit(pScrn);
189
190         if (pNv->Architecture >= NV_ARCH_50)
191                 return;
192
193         switch(pNv->CurrentLayout.depth) {
194         case 24:
195                 surfaceFormat = SURFACE_FORMAT_X8R8G8B8;
196                 patternFormat = PATTERN_FORMAT_DEPTH24;
197                 rectFormat = RECT_FORMAT_DEPTH24;
198                 lineFormat = LINE_FORMAT_DEPTH24;
199                 break;
200         case 16:
201         case 15:
202                 surfaceFormat = SURFACE_FORMAT_R5G6B5;
203                 patternFormat = PATTERN_FORMAT_DEPTH16;
204                 rectFormat = RECT_FORMAT_DEPTH16;
205                 lineFormat = LINE_FORMAT_DEPTH16;
206                 break;
207         default:
208                 surfaceFormat = SURFACE_FORMAT_Y8;
209                 patternFormat = PATTERN_FORMAT_DEPTH8;
210                 rectFormat = RECT_FORMAT_DEPTH8;
211                 lineFormat = LINE_FORMAT_DEPTH8;
212                 break;
213         }
214
215         NVDmaStart(pNv, NvContextSurfaces, SURFACE_FORMAT, 4);
216         NVDmaNext (pNv, surfaceFormat);
217         NVDmaNext (pNv, pitch | (pitch << 16));
218         NVDmaNext (pNv, (uint32_t)pNv->FB->offset);
219         NVDmaNext (pNv, (uint32_t)pNv->FB->offset);
220
221         NVDmaStart(pNv, NvImagePattern, PATTERN_FORMAT, 1);
222         NVDmaNext (pNv, patternFormat);
223
224         NVDmaStart(pNv, NvRectangle, RECT_FORMAT, 1);
225         NVDmaNext (pNv, rectFormat);
226
227         if (!pNv->useEXA) {
228                 NVDmaStart(pNv, NvSolidLine, LINE_FORMAT, 1);
229                 NVDmaNext (pNv, lineFormat);
230         }
231
232         pNv->currentRop = ~0;  /* set to something invalid */
233         NVSetRopSolid(pScrn, GXcopy, ~0);
234
235         pNv->M2MFDirection = -1; /* invalid */
236         /*NVDmaKickoff(pNv);*/
237 }
238
239 Bool NVDmaCreateContextObject(NVPtr pNv, int handle, int class)
240 {
241         struct drm_nouveau_grobj_alloc cto;
242         int ret;
243
244         cto.channel = pNv->fifo.channel;
245         cto.handle  = handle;
246         cto.class   = class;
247         ret = drmCommandWrite(pNv->drm_fd, DRM_NOUVEAU_GROBJ_ALLOC,
248                                            &cto, sizeof(cto));
249         return ret == 0;
250 }
251
252 static void NVInitDmaCB(ScrnInfoPtr pScrn)
253 {
254         NVPtr pNv = NVPTR(pScrn);
255         unsigned int cb_location;
256         int cb_size;
257         char *s;
258
259         /* I'm not bothering to check for failures here, the DRM will fall back
260          * on defaults if anything's wrong (ie. out of AGP, invalid sizes)
261          */
262 #ifndef __powerpc__
263         if (pNv->GARTScratch)
264                 cb_location = NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI_ACCEPTABLE;
265         else
266 #endif
267         cb_location = NOUVEAU_MEM_FB;
268         if((s = (char *)xf86GetOptValString(pNv->Options, OPTION_CMDBUF_LOCATION))) {
269                 if(!xf86NameCmp(s, "AGP"))
270                         cb_location = NOUVEAU_MEM_AGP;
271                 else if (!xf86NameCmp(s, "VRAM"))
272                         cb_location = NOUVEAU_MEM_FB;
273                 else if (!xf86NameCmp(s, "PCI"))
274                         cb_location = NOUVEAU_MEM_PCI;
275                 else
276                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid value \"%s\" for CBLocation\n", s);
277         }
278         NVDRMSetParam(pNv, NOUVEAU_SETPARAM_CMDBUF_LOCATION, cb_location);
279
280         /* CBSize == size of space reserved for *all* FIFOs in MiB */
281         if (xf86GetOptValInteger(pNv->Options, OPTION_CMDBUF_SIZE, &cb_size))
282                 NVDRMSetParam(pNv, NOUVEAU_SETPARAM_CMDBUF_SIZE, (cb_size<<20));
283 }
284
285 Bool NVInitDma(ScrnInfoPtr pScrn)
286 {
287         NVPtr pNv = NVPTR(pScrn);
288         int i, ret;
289
290         NVInitDmaCB(pScrn);
291
292         if (pNv->NoAccel)
293                 return TRUE;
294
295         pNv->fifo.fb_ctxdma_handle = NvDmaFB;
296         pNv->fifo.tt_ctxdma_handle = NvDmaTT;
297         ret = drmCommandWriteRead(pNv->drm_fd, DRM_NOUVEAU_CHANNEL_ALLOC,
298                                   &pNv->fifo, sizeof(pNv->fifo));
299         if (ret) {
300                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
301                            "Could not allocate GPU channel: %d\n", ret);
302                 return FALSE;
303         }
304
305         ret = drmMap(pNv->drm_fd, pNv->fifo.cmdbuf, pNv->fifo.cmdbuf_size,
306                      (drmAddressPtr)&pNv->dmaBase);
307         if (ret) {
308                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
309                            "Failed to map DMA push buffer: %d\n", ret);
310                 return FALSE;
311         }
312
313         ret = drmMap(pNv->drm_fd, pNv->fifo.ctrl, pNv->fifo.ctrl_size,
314                      (drmAddressPtr)&pNv->FIFO);
315         if (ret) {
316                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
317                            "Failed to map FIFO control regs: %d\n", ret);
318                 return FALSE;
319         }
320
321         ret = drmMap(pNv->drm_fd, pNv->fifo.notifier, pNv->fifo.notifier_size,
322                      (drmAddressPtr)&pNv->NotifierBlock);
323         if (ret) {
324                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
325                            "Failed to map notifier block: %d\n", ret);
326                 return FALSE;
327         }
328
329         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
330                    "Using FIFO channel %d\n", pNv->fifo.channel);
331         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
332                    "  Control registers : %p (0x%08x)\n",
333                    pNv->FIFO, pNv->fifo.ctrl);
334         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
335                    "  DMA command buffer: %p (0x%08x)\n",
336                    pNv->dmaBase, pNv->fifo.cmdbuf);
337         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
338                    "  DMA cmdbuf length : %d KiB\n",
339                    pNv->fifo.cmdbuf_size / 1024);
340         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
341                    "  DMA base PUT      : 0x%08x\n", pNv->fifo.put_base);
342
343         pNv->dmaPut = pNv->dmaCurrent = READ_GET(pNv);
344         pNv->dmaMax = (pNv->fifo.cmdbuf_size >> 2) - 2;
345         pNv->dmaFree = pNv->dmaMax - pNv->dmaCurrent;
346
347         for (i=0; i<SKIPS; i++)
348                 NVDmaNext(pNv,0);
349         pNv->dmaFree -= SKIPS;
350
351         return TRUE;
352 }
353