some more work on nv30 exa...
[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         int i;
166
167         pNv->dmaPut = pNv->dmaCurrent = READ_GET(pNv);
168         pNv->dmaMax = (pNv->fifo.cmdbuf_size >> 2) - 2;
169         pNv->dmaFree = pNv->dmaMax - pNv->dmaCurrent;
170
171         /* assert there's enough room for the skips */
172         if(pNv->dmaFree <= SKIPS)
173                 NVDmaWait(pScrn, SKIPS); 
174         for (i=0; i<SKIPS; i++) {
175                 NVDmaNext(pNv,0);
176                 pNv->dmaBase[i]=0;
177         }
178         pNv->dmaFree -= SKIPS;
179
180         for(i=0;i<8;i++)
181                 subchannels[i]=0;
182
183         NVAccelCommonInit(pScrn);
184 }
185
186 Bool NVDmaCreateContextObject(NVPtr pNv, int handle, int class)
187 {
188         struct drm_nouveau_grobj_alloc cto;
189         int ret;
190
191         cto.channel = pNv->fifo.channel;
192         cto.handle  = handle;
193         cto.class   = class;
194         ret = drmCommandWrite(pNv->drm_fd, DRM_NOUVEAU_GROBJ_ALLOC,
195                                            &cto, sizeof(cto));
196         return ret == 0;
197 }
198
199 static void NVInitDmaCB(ScrnInfoPtr pScrn)
200 {
201         NVPtr pNv = NVPTR(pScrn);
202         unsigned int cb_location;
203         int cb_size;
204         char *s;
205
206         /* I'm not bothering to check for failures here, the DRM will fall back
207          * on defaults if anything's wrong (ie. out of AGP, invalid sizes)
208          */
209 #ifndef __powerpc__
210         if (pNv->GARTScratch)
211                 cb_location = NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI_ACCEPTABLE;
212         else
213 #endif
214         cb_location = NOUVEAU_MEM_FB;
215         if((s = (char *)xf86GetOptValString(pNv->Options, OPTION_CMDBUF_LOCATION))) {
216                 if(!xf86NameCmp(s, "AGP"))
217                         cb_location = NOUVEAU_MEM_AGP;
218                 else if (!xf86NameCmp(s, "VRAM"))
219                         cb_location = NOUVEAU_MEM_FB;
220                 else if (!xf86NameCmp(s, "PCI"))
221                         cb_location = NOUVEAU_MEM_PCI;
222                 else
223                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Invalid value \"%s\" for CBLocation\n", s);
224         }
225         NVDRMSetParam(pNv, NOUVEAU_SETPARAM_CMDBUF_LOCATION, cb_location);
226
227         /* CBSize == size of space reserved for *all* FIFOs in MiB */
228         if (xf86GetOptValInteger(pNv->Options, OPTION_CMDBUF_SIZE, &cb_size))
229                 NVDRMSetParam(pNv, NOUVEAU_SETPARAM_CMDBUF_SIZE, (cb_size<<20));
230 }
231
232 Bool NVInitDma(ScrnInfoPtr pScrn)
233 {
234         NVPtr pNv = NVPTR(pScrn);
235         int i, ret;
236
237         NVInitDmaCB(pScrn);
238
239         if (pNv->NoAccel)
240                 return TRUE;
241
242         pNv->fifo.fb_ctxdma_handle = NvDmaFB;
243         pNv->fifo.tt_ctxdma_handle = NvDmaTT;
244         ret = drmCommandWriteRead(pNv->drm_fd, DRM_NOUVEAU_CHANNEL_ALLOC,
245                                   &pNv->fifo, sizeof(pNv->fifo));
246         if (ret) {
247                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
248                            "Could not allocate GPU channel: %d\n", ret);
249                 return FALSE;
250         }
251
252         ret = drmMap(pNv->drm_fd, pNv->fifo.cmdbuf, pNv->fifo.cmdbuf_size,
253                      (drmAddressPtr)&pNv->dmaBase);
254         if (ret) {
255                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
256                            "Failed to map DMA push buffer: %d\n", ret);
257                 return FALSE;
258         }
259
260         ret = drmMap(pNv->drm_fd, pNv->fifo.ctrl, pNv->fifo.ctrl_size,
261                      (drmAddressPtr)&pNv->FIFO);
262         if (ret) {
263                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
264                            "Failed to map FIFO control regs: %d\n", ret);
265                 return FALSE;
266         }
267
268         ret = drmMap(pNv->drm_fd, pNv->fifo.notifier, pNv->fifo.notifier_size,
269                      (drmAddressPtr)&pNv->NotifierBlock);
270         if (ret) {
271                 xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
272                            "Failed to map notifier block: %d\n", ret);
273                 return FALSE;
274         }
275
276         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
277                    "Using FIFO channel %d\n", pNv->fifo.channel);
278         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
279                    "  Control registers : %p (0x%08x)\n",
280                    pNv->FIFO, pNv->fifo.ctrl);
281         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
282                    "  DMA command buffer: %p (0x%08x)\n",
283                    pNv->dmaBase, pNv->fifo.cmdbuf);
284         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
285                    "  DMA cmdbuf length : %d KiB\n",
286                    pNv->fifo.cmdbuf_size / 1024);
287         xf86DrvMsg(pScrn->scrnIndex, X_INFO,
288                    "  DMA base PUT      : 0x%08x\n", pNv->fifo.put_base);
289
290         pNv->dmaPut = pNv->dmaCurrent = READ_GET(pNv);
291         pNv->dmaMax = (pNv->fifo.cmdbuf_size >> 2) - 2;
292         pNv->dmaFree = pNv->dmaMax - pNv->dmaCurrent;
293
294         for (i=0; i<SKIPS; i++)
295                 NVDmaNext(pNv,0);
296         pNv->dmaFree -= SKIPS;
297
298         return TRUE;
299 }
300