Merge branch 'upstream-gallium-0.1' into nouveau-gallium-0.1
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_bo.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 <stdint.h>
24 #include <stdlib.h>
25 #include <errno.h>
26 #include <assert.h>
27
28 #include "nouveau_drmif.h"
29 #include "nouveau_dma.h"
30 #include "nouveau_local.h"
31
32 static void
33 nouveau_mem_free(struct nouveau_device *dev, struct drm_nouveau_mem_alloc *ma,
34 void **map)
35 {
36 struct nouveau_device_priv *nvdev = nouveau_device(dev);
37 struct drm_nouveau_mem_free mf;
38
39 if (map && *map) {
40 drmUnmap(*map, ma->size);
41 *map = NULL;
42 }
43
44 if (ma->size) {
45 mf.offset = ma->offset;
46 mf.flags = ma->flags;
47 drmCommandWrite(nvdev->fd, DRM_NOUVEAU_MEM_FREE,
48 &mf, sizeof(mf));
49 ma->size = 0;
50 }
51 }
52
53 static int
54 nouveau_mem_alloc(struct nouveau_device *dev, unsigned size, unsigned align,
55 uint32_t flags, struct drm_nouveau_mem_alloc *ma, void **map)
56 {
57 struct nouveau_device_priv *nvdev = nouveau_device(dev);
58 int ret;
59
60 ma->alignment = align;
61 ma->size = size;
62 ma->flags = flags;
63 if (map)
64 ma->flags |= NOUVEAU_MEM_MAPPED;
65 ret = drmCommandWriteRead(nvdev->fd, DRM_NOUVEAU_MEM_ALLOC, ma,
66 sizeof(struct drm_nouveau_mem_alloc));
67 if (ret)
68 return ret;
69
70 if (map) {
71 ret = drmMap(nvdev->fd, ma->map_handle, ma->size, map);
72 if (ret) {
73 *map = NULL;
74 nouveau_mem_free(dev, ma, map);
75 return ret;
76 }
77 }
78
79 return 0;
80 }
81
82 static void
83 nouveau_bo_tmp_del(void *priv)
84 {
85 struct nouveau_resource *r = priv;
86
87 nouveau_fence_ref(NULL, (struct nouveau_fence **)&r->priv);
88 nouveau_resource_free(&r);
89 }
90
91 static struct nouveau_resource *
92 nouveau_bo_tmp(struct nouveau_channel *chan, unsigned size,
93 struct nouveau_fence *fence)
94 {
95 struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
96 struct nouveau_resource *r = NULL;
97 struct nouveau_fence *ref = NULL;
98
99 if (fence)
100 nouveau_fence_ref(fence, &ref);
101 else
102 nouveau_fence_new(chan, &ref);
103 assert(ref);
104
105 while (nouveau_resource_alloc(nvdev->sa_heap, size, ref, &r)) {
106 nouveau_fence_flush(chan);
107 }
108 nouveau_fence_signal_cb(ref, nouveau_bo_tmp_del, r);
109
110 return r;
111 }
112
113 int
114 nouveau_bo_init(struct nouveau_device *dev)
115 {
116 struct nouveau_device_priv *nvdev = nouveau_device(dev);
117 int ret;
118
119 ret = nouveau_mem_alloc(dev, 128*1024, 0, NOUVEAU_MEM_AGP |
120 NOUVEAU_MEM_PCI, &nvdev->sa, &nvdev->sa_map);
121 if (ret)
122 return ret;
123
124 ret = nouveau_resource_init(&nvdev->sa_heap, 0, nvdev->sa.size);
125 if (ret) {
126 nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
127 return ret;
128 }
129
130 return 0;
131 }
132
133 void
134 nouveau_bo_takedown(struct nouveau_device *dev)
135 {
136 struct nouveau_device_priv *nvdev = nouveau_device(dev);
137
138 nouveau_mem_free(dev, &nvdev->sa, &nvdev->sa_map);
139 }
140
141 int
142 nouveau_bo_new(struct nouveau_device *dev, uint32_t flags, int align,
143 int size, struct nouveau_bo **bo)
144 {
145 struct nouveau_bo_priv *nvbo;
146 int ret;
147
148 if (!dev || !bo || *bo)
149 return -EINVAL;
150
151 nvbo = calloc(1, sizeof(struct nouveau_bo_priv));
152 if (!nvbo)
153 return -ENOMEM;
154 nvbo->base.device = dev;
155 nvbo->base.size = size;
156 nvbo->base.handle = bo_to_ptr(nvbo);
157 nvbo->drm.alignment = align;
158 nvbo->refcount = 1;
159
160 ret = nouveau_bo_set_status(&nvbo->base, flags);
161 if (ret) {
162 free(nvbo);
163 return ret;
164 }
165
166 *bo = &nvbo->base;
167 return 0;
168 }
169
170 int
171 nouveau_bo_user(struct nouveau_device *dev, void *ptr, int size,
172 struct nouveau_bo **bo)
173 {
174 struct nouveau_bo_priv *nvbo;
175
176 if (!dev || !bo || *bo)
177 return -EINVAL;
178
179 nvbo = calloc(1, sizeof(*nvbo));
180 if (!nvbo)
181 return -ENOMEM;
182 nvbo->base.device = dev;
183
184 nvbo->sysmem = ptr;
185 nvbo->user = 1;
186
187 nvbo->base.size = size;
188 nvbo->base.offset = nvbo->drm.offset;
189 nvbo->base.handle = bo_to_ptr(nvbo);
190 nvbo->refcount = 1;
191 *bo = &nvbo->base;
192 return 0;
193 }
194
195 int
196 nouveau_bo_ref(struct nouveau_device *dev, uint64_t handle,
197 struct nouveau_bo **bo)
198 {
199 struct nouveau_bo_priv *nvbo = ptr_to_bo(handle);
200
201 if (!dev || !bo || *bo)
202 return -EINVAL;
203
204 nvbo->refcount++;
205 *bo = &nvbo->base;
206 return 0;
207 }
208
209 void
210 nouveau_bo_del(struct nouveau_bo **bo)
211 {
212 struct nouveau_bo_priv *nvbo;
213
214 if (!bo || !*bo)
215 return;
216 nvbo = nouveau_bo(*bo);
217 *bo = NULL;
218
219 if (--nvbo->refcount)
220 return;
221
222 if (nvbo->fence)
223 nouveau_fence_wait(&nvbo->fence);
224 nouveau_mem_free(nvbo->base.device, &nvbo->drm, &nvbo->map);
225 if (nvbo->sysmem && !nvbo->user)
226 free(nvbo->sysmem);
227 free(nvbo);
228 }
229
230 int
231 nouveau_bo_map(struct nouveau_bo *bo, uint32_t flags)
232 {
233 struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
234
235 if (!nvbo)
236 return -EINVAL;
237
238 if (flags & NOUVEAU_BO_WR)
239 nouveau_fence_wait(&nvbo->fence);
240 else
241 nouveau_fence_wait(&nvbo->wr_fence);
242
243 if (nvbo->sysmem)
244 bo->map = nvbo->sysmem;
245 else
246 bo->map = nvbo->map;
247 return 0;
248 }
249
250 void
251 nouveau_bo_unmap(struct nouveau_bo *bo)
252 {
253 bo->map = NULL;
254 }
255
256 static int
257 nouveau_bo_upload(struct nouveau_bo_priv *nvbo)
258 {
259 if (nvbo->fence)
260 nouveau_fence_wait(&nvbo->fence);
261 memcpy(nvbo->map, nvbo->sysmem, nvbo->drm.size);
262 return 0;
263 }
264
265 int
266 nouveau_bo_set_status(struct nouveau_bo *bo, uint32_t flags)
267 {
268 struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
269 struct drm_nouveau_mem_alloc new;
270 void *new_map = NULL, *new_sysmem = NULL;
271 unsigned new_flags = 0, ret;
272
273 assert(!bo->map);
274
275 /* Check current memtype vs requested, if they match do nothing */
276 if ((nvbo->drm.flags & NOUVEAU_MEM_FB) && (flags & NOUVEAU_BO_VRAM))
277 return 0;
278 if ((nvbo->drm.flags & NOUVEAU_MEM_AGP) && (flags & NOUVEAU_BO_GART))
279 return 0;
280 if (nvbo->drm.size == 0 && nvbo->sysmem && (flags & NOUVEAU_BO_LOCAL))
281 return 0;
282
283 memset(&new, 0x00, sizeof(new));
284
285 /* Allocate new memory */
286 if (flags & NOUVEAU_BO_VRAM)
287 new_flags |= NOUVEAU_MEM_FB;
288 else
289 if (flags & NOUVEAU_BO_GART)
290 new_flags |= (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI);
291
292 if (new_flags) {
293 ret = nouveau_mem_alloc(bo->device, bo->size,
294 nvbo->drm.alignment, new_flags,
295 &new, &new_map);
296 if (ret)
297 return ret;
298 } else {
299 new_sysmem = malloc(bo->size);
300 }
301
302 /* Copy old -> new */
303 /*XXX: use M2MF */
304 if (nvbo->sysmem || nvbo->map) {
305 nouveau_bo_map(bo, NOUVEAU_BO_RD);
306 memcpy(new_map, bo->map, bo->size);
307 nouveau_bo_unmap(bo);
308 }
309
310 /* Free old memory */
311 if (nvbo->fence)
312 nouveau_fence_wait(&nvbo->fence);
313 nouveau_mem_free(bo->device, &nvbo->drm, &nvbo->map);
314 if (nvbo->sysmem)
315 free(nvbo->sysmem);
316
317 nvbo->drm = new;
318 nvbo->map = new_map;
319 nvbo->sysmem = new_sysmem;
320 bo->flags = flags;
321 bo->offset = nvbo->drm.offset;
322 return 0;
323 }
324
325 static int
326 nouveau_bo_validate_user(struct nouveau_channel *chan, struct nouveau_bo *bo,
327 struct nouveau_fence *fence, uint32_t flags)
328 {
329 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
330 struct nouveau_device_priv *nvdev = nouveau_device(chan->device);
331 struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
332 struct nouveau_resource *r;
333
334 if (nvchan->user_charge + bo->size > nvdev->sa.size)
335 return 1;
336 nvchan->user_charge += bo->size;
337
338 if (!(flags & NOUVEAU_BO_GART))
339 return 1;
340
341 r = nouveau_bo_tmp(chan, bo->size, fence);
342 if (!r)
343 return 1;
344
345 memcpy(nvdev->sa_map + r->start, nvbo->sysmem, bo->size);
346
347 nvbo->offset = nvdev->sa.offset + r->start;
348 nvbo->flags = NOUVEAU_BO_GART;
349 return 0;
350 }
351
352 static int
353 nouveau_bo_validate_bo(struct nouveau_channel *chan, struct nouveau_bo *bo,
354 struct nouveau_fence *fence, uint32_t flags)
355 {
356 struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
357 int ret;
358
359 ret = nouveau_bo_set_status(bo, flags);
360 if (ret)
361 return ret;
362
363 if (nvbo->user)
364 nouveau_bo_upload(nvbo);
365
366 nvbo->offset = nvbo->drm.offset;
367 if (nvbo->drm.flags & (NOUVEAU_MEM_AGP | NOUVEAU_MEM_PCI))
368 nvbo->flags = NOUVEAU_BO_GART;
369 else
370 nvbo->flags = NOUVEAU_BO_VRAM;
371
372 return 0;
373 }
374
375 int
376 nouveau_bo_validate(struct nouveau_channel *chan, struct nouveau_bo *bo,
377 struct nouveau_fence *fence, uint32_t flags)
378 {
379 struct nouveau_bo_priv *nvbo = nouveau_bo(bo);
380 int ret;
381
382 assert(bo->map == NULL);
383
384 if (nvbo->user) {
385 ret = nouveau_bo_validate_user(chan, bo, fence, flags);
386 if (ret) {
387 ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
388 if (ret)
389 return ret;
390 }
391 } else {
392 ret = nouveau_bo_validate_bo(chan, bo, fence, flags);
393 if (ret)
394 return ret;
395 }
396
397 if (flags & NOUVEAU_BO_WR)
398 nouveau_fence_ref(fence, &nvbo->wr_fence);
399 nouveau_fence_ref(fence, &nvbo->fence);
400 return 0;
401 }
402