remove nouveau_dma, use hackish kernel submission ioctl now.
[nouveau] / src / nouveau_pushbuf.c
1 /*
2  * Copyright 2007 Nouveau Project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
17  * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19  * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  */
22
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <assert.h>
26
27 #include "nouveau_drmif.h"
28
29 #define PB_BUFMGR_DWORDS   (4096 / 2)
30 #define PB_MIN_USER_DWORDS  2048
31
32 static int
33 nouveau_pushbuf_space(struct nouveau_channel *chan, unsigned min)
34 {
35         struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
36         struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
37
38         if (nvpb->pushbuf)
39                 free(nvpb->pushbuf);
40
41         nvpb->size = min < PB_MIN_USER_DWORDS ? PB_MIN_USER_DWORDS : min;       
42         nvpb->pushbuf = malloc(sizeof(uint32_t) * nvpb->size);
43
44         nvpb->base.channel = chan;
45         nvpb->base.remaining = nvpb->size;
46         nvpb->base.cur = nvpb->pushbuf;
47
48         /* Create a new fence object for this "frame" */
49         nouveau_fence_ref(NULL, &nvpb->base.fence);
50         nouveau_fence_new(chan, &nvpb->base.fence);
51
52         return 0;
53 }
54
55 int
56 nouveau_pushbuf_init(struct nouveau_channel *chan)
57 {
58         struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
59
60         nouveau_pushbuf_space(chan, 0);
61
62         nvchan->pb.buffers = calloc(NOUVEAU_PUSHBUF_MAX_BUFFERS,
63                                     sizeof(struct nouveau_pushbuf_bo));
64         nvchan->pb.relocs = calloc(NOUVEAU_PUSHBUF_MAX_RELOCS,
65                                    sizeof(struct nouveau_pushbuf_reloc));
66         
67         chan->pushbuf = &nvchan->pb.base;
68         return 0;
69 }
70
71 static uint32_t
72 nouveau_pushbuf_calc_reloc(struct nouveau_bo *bo,
73                            struct nouveau_pushbuf_reloc *r)
74 {
75         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
76         uint32_t push;
77
78         if (r->flags & NOUVEAU_BO_LOW) {
79                 push = nvbo->offset + r->data;
80         } else
81         if (r->flags & NOUVEAU_BO_HIGH) {
82                 push = (nvbo->offset + r->data) >> 32;
83         } else {
84                 push = r->data;
85         }
86
87         if (r->flags & NOUVEAU_BO_OR) {
88                 if (nvbo->domain & NOUVEAU_GEM_DOMAIN_VRAM)
89                         push |= r->vor;
90                 else
91                         push |= r->tor;
92         }
93
94         return push;
95 }
96
97 /* This would be our TTM "superioctl" */
98 int
99 nouveau_pushbuf_flush(struct nouveau_channel *chan, unsigned min)
100 {
101         struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
102         struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
103         struct nouveau_pushbuf_priv *nvpb = &nvchan->pb;
104         int ret, i;
105
106         if (nvpb->base.remaining == nvpb->size)
107                 return 0;
108         nvpb->size -= nvpb->base.remaining;
109
110         nouveau_fence_flush(chan);
111
112         /* Validate buffers + apply relocations */
113         nvchan->user_charge = 0;
114         for (i = 0; i < nvpb->nr_relocs; i++) {
115                 struct nouveau_pushbuf_reloc *r = &nvpb->relocs[i];
116                 struct nouveau_pushbuf_bo *pbbo = r->pbbo;
117                 struct nouveau_bo *bo = pbbo->bo;
118
119                 /* Validated, mem matches presumed, no relocation necessary */
120                 if (pbbo->handled & 2) {
121                         if (!(pbbo->handled & 1))
122                                 assert(0);
123                         continue;
124                 }
125
126                 /* Not yet validated, do it now */
127                 if (!(pbbo->handled & 1)) {
128                         uint64_t offset = nouveau_bo(bo)->offset;
129                         unsigned domain = nouveau_bo(bo)->domain;
130
131                         ret = nouveau_bo_validate(chan, bo, pbbo->flags);
132                         if (ret) {
133                                 assert(0);
134                                 return ret;
135                         }
136                         pbbo->handled |= 1;
137
138                         if (offset == nouveau_bo(bo)->offset &&
139                             domain == nouveau_bo(bo)->domain) {
140                                 pbbo->handled |= 2;
141                                 continue;
142                         }
143                 }
144
145                 /* Apply the relocation */
146                 *r->ptr = nouveau_pushbuf_calc_reloc(bo, r);
147         }
148         nvpb->nr_relocs = 0;
149
150         /* Dereference all buffers on validate list */
151         for (i = 0; i < nvpb->nr_buffers; i++) {
152                 struct nouveau_pushbuf_bo *pbbo = &nvpb->buffers[i];
153
154                 nouveau_bo(pbbo->bo)->pending = NULL;
155                 nouveau_bo_del(&pbbo->bo);
156         }
157         nvpb->nr_buffers = 0;
158
159         /* Fence + kickoff */
160         nouveau_fence_emit(nvpb->base.fence);
161
162         {
163                 struct drm_nouveau_gem_pushbuf req;
164
165                 req.channel = chan->id;
166                 req.size = nvpb->size;
167                 req.ptr = (uint32_t)(unsigned long)nvpb->pushbuf;
168                 ret = drmCommandWrite(nvdev->fd, DRM_NOUVEAU_GEM_PUSHBUF,
169                                       &req, sizeof(req));
170                 assert(ret == 0);
171         }
172
173         /* Allocate space for next push buffer */
174         ret = nouveau_pushbuf_space(chan, min);
175         assert(!ret);
176
177         return 0;
178 }
179
180 static struct nouveau_pushbuf_bo *
181 nouveau_pushbuf_emit_buffer(struct nouveau_channel *chan, struct nouveau_bo *bo)
182 {
183         struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
184         struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
185         struct nouveau_pushbuf_bo *pbbo;
186
187         if (nvbo->pending)
188                 return nvbo->pending;
189
190         if (nvpb->nr_buffers >= NOUVEAU_PUSHBUF_MAX_BUFFERS)
191                 return NULL;
192         pbbo = nvpb->buffers + nvpb->nr_buffers++;
193         nvbo->pending = pbbo;
194
195         nouveau_bo_ref(bo->device, bo->handle, &pbbo->bo);
196         pbbo->channel = chan;
197         pbbo->flags = NOUVEAU_BO_VRAM | NOUVEAU_BO_GART;
198         pbbo->handled = 0;
199         return pbbo;
200 }
201
202 int
203 nouveau_pushbuf_emit_reloc(struct nouveau_channel *chan, void *ptr,
204                            struct nouveau_bo *bo, uint32_t data, uint32_t flags,
205                            uint32_t vor, uint32_t tor)
206 {
207         struct nouveau_pushbuf_priv *nvpb = nouveau_pushbuf(chan->pushbuf);
208         struct nouveau_pushbuf_bo *pbbo;
209         struct nouveau_pushbuf_reloc *r;
210
211         if (nvpb->nr_relocs >= NOUVEAU_PUSHBUF_MAX_RELOCS)
212                 return -ENOMEM;
213
214         pbbo = nouveau_pushbuf_emit_buffer(chan, bo);
215         if (!pbbo)
216                 return -ENOMEM;
217         pbbo->flags |= (flags & NOUVEAU_BO_RDWR);
218         pbbo->flags &= (flags | NOUVEAU_BO_RDWR);
219
220         r = nvpb->relocs + nvpb->nr_relocs++;
221         r->pbbo = pbbo;
222         r->ptr = ptr;
223         r->flags = flags;
224         r->data = data;
225         r->vor = vor;
226         r->tor = tor;
227
228         if (flags & NOUVEAU_BO_DUMMY)
229                 *(uint32_t *)ptr = 0;
230         else
231                 *(uint32_t *)ptr = nouveau_pushbuf_calc_reloc(bo, r);
232         return 0;
233 }
234