Merge branch 'master' of git+ssh://git.freedesktop.org/git/nouveau/xf86-video-nouveau
[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     NVDmaCreateContextObject (pNv, NvScaledImage,
220                               NV_SCALED_IMAGE_FROM_MEMORY, 
221                               NV_DMA_CONTEXT_FLAGS_PATCH_SRCCOPY, 
222                               NvDmaFB, NvDmaFB, 0);
223
224 #ifdef XF86DRI
225     if (NVInitAGP(pScrn) && pNv->agpScratch) {
226         pNv->Notifier0 = NVDmaCreateNotifier(pNv, NvDmaNotifier0);
227                 if (pNv->Notifier0) {
228                         NVDmaCreateDMAObject(pNv, NvDmaAGP, NV_DMA_TARGET_AGP,
229                                         pNv->agpScratchPhysical, pNv->agpScratchSize, NV_DMA_ACCES_RW);
230
231                 NVDmaCreateContextObject (pNv, NvGraphicsToAGP,
232                                         NV_MEMORY_TO_MEMORY_FORMAT, 0,
233                                         NvDmaFB, NvDmaAGP, NvDmaNotifier0
234                                         );
235         
236             NVDmaCreateContextObject (pNv, NvAGPToGraphics,
237                                         NV_MEMORY_TO_MEMORY_FORMAT, 0,
238                                         NvDmaAGP, NvDmaFB, NvDmaNotifier0
239                                         );
240                 } else {
241                         /* FIXME */
242                         xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Failed to create DMA notifier - DMA transfers disabled\n");
243                         pNv->agpScratch = NULL;
244                 }
245     }
246 #endif
247
248     return TRUE;
249 }