Merge branch 'master' of ssh://marcheu@git.freedesktop.org/git/nouveau/xf86-video...
[nouveau] / src / nv_dma.c
1 #include <errno.h>
2 #include "nv_include.h"
3 #include "nvreg.h"
4
5 void NVDmaCreateDMAObject(NVPtr pNv, int handle, int target, CARD32 base_address, CARD32 size, int access)
6 {
7     drm_nouveau_dma_object_init_t dma;
8
9     dma.handle = handle;
10     dma.access = access;
11     dma.target = target;
12     dma.size   = size;
13     dma.offset = base_address;
14     drmCommandWrite(pNv->drm_fd, DRM_NOUVEAU_DMA_OBJECT_INIT, &dma, sizeof(dma));
15 }
16
17 /*
18    A DMA notifier is a DMA object that references a small (32 byte it
19    seems, we use 256 for saftey) memory area that will be used by the HW to give feedback
20    about a DMA operation.
21 */
22 void *NVDmaCreateNotifier(NVPtr pNv, int handle)
23 {
24         uint64_t notifier_base;
25         void *notifier = NULL;
26         int target = 0;
27
28 #ifndef __powerpc__
29         if (pNv->AGPScratch) {
30                 drm_nouveau_mem_alloc_t alloc;
31
32                 alloc.flags     = NOUVEAU_MEM_AGP|NOUVEAU_MEM_MAPPED;
33                 alloc.alignment = 0;
34                 alloc.size      = 256;
35                 alloc.region_offset = &notifier_base;
36                 if (!(drmCommandWriteRead(pNv->drm_fd, DRM_NOUVEAU_MEM_ALLOC,
37                                                 &alloc, sizeof(alloc)))) {
38                         if (!drmMap(pNv->drm_fd, notifier_base, alloc.size, &notifier))
39                                 target = NV_DMA_TARGET_AGP;
40                 }
41         }
42 #endif
43
44         if (!target) /* FIXME: try a FB notifier when we can alloc the memory */
45                 return NULL;
46
47         NVDmaCreateDMAObject(pNv, handle, target, notifier_base, 256, NV_DMA_ACCES_RW);
48         return notifier;
49 }
50
51 /*
52   How do we wait for DMA completion (by notifiers) ?
53
54    Either repeatedly read the notifier address and wait until it changes,
55    or enable a 'wakeup' interrupt by writing NOTIFY_WRITE_LE_AWAKEN into
56    the 'notify' field of the object in the channel.  My guess is that 
57    this causes an interrupt in PGRAPH/NOTIFY as soon as the transfer is
58    completed.  Clients probably can use poll on the nv* devices to get this 
59    event.  All this is a guess.  I don't know any details, and I have not
60    tested is.  Also, I have no idea how the 'nvdriver' reacts if it gets 
61    notify events that are not registered.
62
63    Writing NV_NOTIFY_WRITE_LE_AWAKEN into the 'Notify' field of an object
64    in a channel really causes an interrupt in the PGRAPH engine.  Thus
65    we can determine whether a DMA transfer has finished in the interrupt
66    handler.
67
68    We can't use interrupts in user land, so we do the simple polling approach.
69    The method returns FALSE in case of an error.
70 */
71 Bool NVDmaWaitForNotifier(NVPtr pNv, void *notifier)
72 {
73     int t_start, timeout = 0;
74     volatile CARD32 *n;
75
76     n = (volatile CARD32 *)notifier;
77     NVDEBUG("NVDmaWaitForNotifier @%p", n);
78     t_start = GetTimeInMillis();
79     while (1) {
80         CARD32 a = n[0];
81         CARD32 b = n[1];
82         CARD32 c = n[2];
83         CARD32 status = n[3];
84         NVDEBUG("status: n[0]=%x, n[1]=%x, n[2]=%x, n[3]=%x\n", a, b, c, status);
85         NVDEBUG("status: GET: 0x%08x\n", READ_GET(pNv));
86
87         if (GetTimeInMillis() - t_start >= 2000) {
88             /* We've timed out, call NVSync() to detect lockups */
89             if (timeout++ == 0) {
90                 NVDoSync(pNv);
91                 /* If we're still here, wait another second for notifier.. */
92                 t_start = GetTimeInMillis() + 1000;
93                 break;
94             }
95
96             /* Still haven't recieved notification, log error */
97             ErrorF("Notifier timeout\n");
98             return FALSE;
99         }
100
101         if (status == 0xffffffff)
102             continue;
103         if (!status)
104             break;
105         if (status & 0xffff)
106             return FALSE;
107     }
108     return TRUE;
109 }
110
111 void NVDmaCreateContextObject(NVPtr pNv, int handle, int class, CARD32 flags,
112                               CARD32 dma_in, CARD32 dma_out, CARD32 dma_notifier)
113 {
114     drm_nouveau_object_init_t cto;
115     CARD32 nv_flags0 = 0, nv_flags1 = 0, nv_flags2 = 0;
116     
117     if (pNv->Architecture >= NV_ARCH_40) {
118         if (flags & NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND)
119             nv_flags0 |= 0x02080000;
120         else if (flags & NV_DMA_CONTEXT_FLAGS_PATCH_SRCCOPY)
121             nv_flags0 |= 0x02080000;
122         if (flags & NV_DMA_CONTEXT_FLAGS_CLIP_ENABLE)
123             nv_flags0 |= 0x00020000;
124 #if X_BYTE_ORDER == X_BIG_ENDIAN
125         if (flags & NV_DMA_CONTEXT_FLAGS_MONO)
126             nv_flags1 |= 0x01000000;
127         nv_flags2 |= 0x01000000;
128 #else
129         if (flags & NV_DMA_CONTEXT_FLAGS_MONO)
130             nv_flags1 |= 0x02000000;
131 #endif
132     } else {
133         if (flags & NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND)
134             nv_flags0 |= 0x01008000;
135         else if (flags & NV_DMA_CONTEXT_FLAGS_PATCH_SRCCOPY)
136             nv_flags0 |= 0x01018000;
137         if (flags & NV_DMA_CONTEXT_FLAGS_CLIP_ENABLE)
138             nv_flags0 |= 0x00002000;
139 #if X_BYTE_ORDER == X_BIG_ENDIAN
140         nv_flags0 |= 0x00080000;
141         if (flags & NV_DMA_CONTEXT_FLAGS_MONO)
142             nv_flags1 |= 0x00000001;
143 #else
144         if (flags & NV_DMA_CONTEXT_FLAGS_MONO)
145             nv_flags1 |= 0x00000002;
146 #endif
147     }
148
149     cto.handle = handle;
150     cto.class  = class;
151     cto.flags0 = nv_flags0;
152     cto.flags1 = nv_flags1;
153     cto.flags2 = nv_flags2;
154     cto.dma0   = dma_in;
155     cto.dma1   = dma_out;
156     cto.dma_notifier = dma_notifier;
157     drmCommandWrite(pNv->drm_fd, DRM_NOUVEAU_OBJECT_INIT, &cto, sizeof(cto));
158 }
159                            
160 Bool NVInitDma(ScrnInfoPtr pScrn)
161 {
162     NVPtr pNv = NVPTR(pScrn);
163     drm_nouveau_fifo_init_t fifo;
164
165     if (!NVDRIScreenInit(pScrn))
166         return FALSE;
167
168     if (drmCommandWriteRead(pNv->drm_fd, DRM_NOUVEAU_FIFO_INIT, &pNv->fifo, sizeof(pNv->fifo)) != 0) {
169         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Could not initialise kernel module\n");
170         return FALSE;
171     }
172
173     if (drmMap(pNv->drm_fd, pNv->fifo.cmdbuf, pNv->fifo.cmdbuf_size, (drmAddressPtr)&pNv->dmaBase)) {
174         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to map DMA command buffer\n");
175         return FALSE;
176     }
177
178     if (drmMap(pNv->drm_fd, pNv->fifo.ctrl, pNv->fifo.ctrl_size, (drmAddressPtr)&pNv->FIFO)) {
179         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to map FIFO control regs\n");
180         return FALSE;
181     }
182
183     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Using FIFO channel %d\n", pNv->fifo.channel);
184     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  Control registers : %p (0x%08x)\n", pNv->FIFO, pNv->fifo.ctrl);
185     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  DMA command buffer: %p (0x%08x)\n", pNv->dmaBase, pNv->fifo.cmdbuf);
186     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  DMA cmdbuf length : %d KiB\n", pNv->fifo.cmdbuf_size / 1024);
187     xf86DrvMsg(pScrn->scrnIndex, X_INFO, "  DMA base PUT      : 0x%08x\n", pNv->fifo.put_base);
188
189     NVDmaCreateDMAObject(pNv, NvDmaFB, NV_DMA_TARGET_VIDMEM, 0, pNv->VRAMPhysicalSize, NV_DMA_ACCES_RW);
190     
191     NVDmaCreateContextObject (pNv, NvContextSurfaces,
192                               (pNv->Architecture >= NV_ARCH_10) ? NV10_CONTEXT_SURFACES_2D : NV4_SURFACE,
193                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND,
194                               NvDmaFB, NvDmaFB, 0);
195     NVDmaCreateContextObject (pNv, NvRop,
196                               NV_ROP5_SOLID, 
197                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND,
198                               0, 0, 0);
199     NVDmaCreateContextObject (pNv, NvImagePattern,
200                               NV4_IMAGE_PATTERN, 
201                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND|NV_DMA_CONTEXT_FLAGS_MONO,
202                               0, 0, 0);
203     NVDmaCreateContextObject (pNv, NvClipRectangle,
204                               NV_IMAGE_BLACK_RECTANGLE, 
205                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND,
206                               0, 0, 0);
207     NVDmaCreateContextObject (pNv, NvSolidLine,
208                               NV4_RENDER_SOLID_LIN, 
209                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND|NV_DMA_CONTEXT_FLAGS_CLIP_ENABLE,
210                               0, 0, 0);
211     NVDmaCreateContextObject (pNv, NvImageBlit,
212                               pNv->WaitVSyncPossible ? NV12_IMAGE_BLIT : NV_IMAGE_BLIT,
213                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND, 
214                               NvDmaFB, NvDmaFB, 0);
215     NVDmaCreateContextObject (pNv, NvRectangle,
216                               NV4_GDI_RECTANGLE_TEXT, 
217                               NV_DMA_CONTEXT_FLAGS_PATCH_ROP_AND|NV_DMA_CONTEXT_FLAGS_MONO, 
218                               0, 0, 0);
219     if (pNv->Chipset==CHIPSET_NV04)
220         NVDmaCreateContextObject (pNv, NvScaledImage,
221                                   NV_SCALED_IMAGE_FROM_MEMORY, 
222                                   NV_DMA_CONTEXT_FLAGS_PATCH_SRCCOPY, 
223                                   NvDmaFB, NvDmaFB, 0);
224     else if (pNv->Architecture==NV_ARCH_04)
225         NVDmaCreateContextObject (pNv, NvScaledImage,
226                                   NV5_SCALED_IMAGE_FROM_MEMORY, 
227                                   NV_DMA_CONTEXT_FLAGS_PATCH_SRCCOPY, 
228                                   NvDmaFB, NvDmaFB, 0);
229     else
230         NVDmaCreateContextObject (pNv, NvScaledImage,
231                                   NV10_SCALED_IMAGE_FROM_MEMORY, 
232                                   NV_DMA_CONTEXT_FLAGS_PATCH_SRCCOPY, 
233                                   NvDmaFB, NvDmaFB, 0);
234
235 #ifdef XF86DRI
236     if (NVInitAGP(pScrn) && pNv->AGPScratch) {
237         pNv->Notifier0 = NVDmaCreateNotifier(pNv, NvDmaNotifier0);
238                 if (pNv->Notifier0) {
239                         NVDmaCreateDMAObject(pNv, NvDmaAGP, NV_DMA_TARGET_AGP,
240                                         pNv->AGPScratch->offset, pNv->AGPScratch->size, NV_DMA_ACCES_RW);
241
242                 NVDmaCreateContextObject (pNv, NvGraphicsToAGP,
243                                         NV_MEMORY_TO_MEMORY_FORMAT, 0,
244                                         NvDmaFB, NvDmaAGP, NvDmaNotifier0
245                                         );
246         
247             NVDmaCreateContextObject (pNv, NvAGPToGraphics,
248                                         NV_MEMORY_TO_MEMORY_FORMAT, 0,
249                                         NvDmaAGP, NvDmaFB, NvDmaNotifier0
250                                         );
251                 } else {
252                         /* FIXME */
253                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to create DMA notifier - DMA transfers disabled\n");
254                         NVFreeMemory(pNv, pNv->AGPScratch);
255                         pNv->AGPScratch = NULL;
256                 }
257     }
258 #endif
259
260     return TRUE;
261 }