main
nouveau.c
1/* wld: nouveau.c
2 *
3 * Copyright (c) 2013, 2014 Michael Forney
4 *
5 * Based in part upon nvc0_exa.c from xf86-video-nouveau, which is:
6 *
7 * Copyright 2007 NVIDIA, Corporation
8 * Copyright 2008 Ben Skeggs
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29#include "drm-private.h"
30#include "drm.h"
31#include "nouveau/g80_2d.xml.h"
32#include "nouveau/g80_defs.xml.h"
33#include "nouveau/nv_object.xml.h"
34#include "pixman.h"
35
36#include <nouveau.h>
37#include <string.h>
38#include <sys/mman.h>
39
40enum nv_architecture {
41 NV_ARCH_50 = 0x50,
42 NV_ARCH_C0 = 0xc0,
43 NV_ARCH_E0 = 0xe0
44};
45
46struct nouveau_context {
47 struct wld_context base;
48 struct nouveau_device *device;
49 struct nouveau_client *client;
50 enum nv_architecture architecture;
51};
52
53struct nouveau_renderer {
54 struct wld_renderer base;
55 struct nouveau_object *channel;
56 struct nouveau_pushbuf *pushbuf;
57 struct nouveau_bufctx *bufctx;
58 struct nouveau_object *nvc0_2d;
59
60 struct nouveau_buffer *target;
61};
62
63struct nouveau_buffer {
64 struct buffer base;
65 struct wld_exporter exporter;
66 struct nouveau_context *context;
67 struct nouveau_bo *bo;
68};
69
70#include "interface/buffer.h"
71#include "interface/context.h"
72#include "interface/renderer.h"
73#define DRM_DRIVER_NAME nouveau
74#include "interface/drm.h"
75IMPL(nouveau_context, wld_context)
76IMPL(nouveau_renderer, wld_renderer)
77IMPL(nouveau_buffer, wld_buffer)
78
79static void
80pack_gray_row_to_mono(uint8_t *dst, const uint8_t *src, uint32_t width)
81{
82 uint32_t x, bytes_per_row;
83
84 bytes_per_row = (width + 7) / 8;
85 memset(dst, 0, bytes_per_row);
86
87 for (x = 0; x < width; ++x) {
88 if (src[x] >= 128)
89 dst[x / 8] |= 0x80 >> (x & 7);
90 }
91}
92
93/**** DRM driver ****/
94bool
95driver_device_supported(uint32_t vendor_id, uint32_t device_id)
96{
97 return vendor_id == 0x10de;
98}
99
100struct wld_context *
101driver_create_context(int drm_fd)
102{
103 struct nouveau_context *context;
104
105 if (!(context = malloc(sizeof *context)))
106 goto error0;
107
108 if (nouveau_device_wrap(drm_fd, 0, &context->device) != 0)
109 goto error1;
110
111 switch (context->device->chipset & ~0xf) {
112 /* TODO: Support NV50
113 case 0x50:
114 case 0x80:
115 case 0x90:
116 case 0xa0:
117 context->architecture = NV_ARCH_50;
118 break;
119 */
120 case 0xc0:
121 case 0xd0:
122 context->architecture = NV_ARCH_C0;
123 break;
124 /* TODO: Support NVE0
125 case 0xe0:
126 case 0xf0:
127 case 0x100:
128 context->architecture = NV_ARCH_E0;
129 break;
130 */
131 default:
132 return NULL;
133 }
134
135 if (nouveau_client_new(context->device, &context->client) != 0)
136 goto error2;
137
138 context_initialize(&context->base, &wld_context_impl);
139
140 return &context->base;
141
142error2:
143 nouveau_device_del(&context->device);
144error1:
145 free(context);
146error0:
147 return NULL;
148}
149
150/**** Context ****/
151static inline bool
152ensure_space(struct nouveau_pushbuf *push, uint32_t count)
153{
154 if (push->end - push->cur > count)
155 return true;
156
157 return nouveau_pushbuf_space(push, count, 0, 0) == 0;
158}
159
160static inline void
161nv_add_dword(struct nouveau_pushbuf *push, uint32_t dword)
162{
163 *push->cur++ = dword;
164}
165
166static inline void
167nv_add_dwords_va(struct nouveau_pushbuf *push, uint16_t count, va_list dwords)
168{
169 while (count--)
170 nv_add_dword(push, va_arg(dwords, uint32_t));
171}
172
173static inline void
174nv_add_data(struct nouveau_pushbuf *push, void *data, uint32_t count)
175{
176 memcpy(push->cur, data, count * 4);
177 push->cur += count;
178}
179
180static inline uint32_t
181nvc0_format(uint32_t format)
182{
183 switch (format) {
184 case WLD_FORMAT_XRGB8888:
185 return G80_SURFACE_FORMAT_BGRX8_UNORM;
186 case WLD_FORMAT_ARGB8888:
187 return G80_SURFACE_FORMAT_BGRA8_UNORM;
188 }
189
190 return 0;
191}
192
193enum {
194 GF100_COMMAND_TYPE_INCREASING = 1,
195 GF100_COMMAND_TYPE_NON_INCREASING = 3,
196 GF100_COMMAND_TYPE_INLINE = 4
197};
198
199enum {
200 GF100_SUBCHANNEL_2D = 3,
201};
202
203static inline uint32_t
204nvc0_command(uint8_t type, uint8_t subchannel, uint16_t method, uint16_t count_or_value)
205{
206 return type << 29 | count_or_value << 16 | subchannel << 13 | method >> 2;
207}
208
209static inline void
210nvc0_inline(struct nouveau_pushbuf *push, uint8_t subchannel, uint16_t method, uint16_t value)
211{
212 nv_add_dword(push, nvc0_command(GF100_COMMAND_TYPE_INLINE,
213 subchannel, method, value));
214}
215
216static inline void
217nvc0_methods(struct nouveau_pushbuf *push,
218 uint8_t subchannel, uint16_t start_method,
219 uint16_t count, ...)
220{
221 va_list dwords;
222 nv_add_dword(push, nvc0_command(GF100_COMMAND_TYPE_INCREASING,
223 subchannel, start_method, count));
224 va_start(dwords, count);
225 nv_add_dwords_va(push, count, dwords);
226 va_end(dwords);
227}
228
229#define nvc0_2d(push, method, count, ...) \
230 nvc0_methods(push, GF100_SUBCHANNEL_2D, method, count, __VA_ARGS__)
231#define nvc0_2d_inline(push, method, value) \
232 nvc0_inline(push, GF100_SUBCHANNEL_2D, method, value)
233
234static bool
235nvc0_2d_initialize(struct nouveau_renderer *renderer)
236{
237 int ret;
238
239 ret = nouveau_object_new(renderer->channel, GF100_2D, GF100_2D, NULL, 0,
240 &renderer->nvc0_2d);
241
242 if (ret != 0)
243 goto error0;
244
245 if (!ensure_space(renderer->pushbuf, 5))
246 goto error1;
247
248 nvc0_2d(renderer->pushbuf, NV1_SUBCHAN_OBJECT, 1,
249 renderer->nvc0_2d->handle);
250 nvc0_2d_inline(renderer->pushbuf, G80_2D_OPERATION,
251 G80_2D_OPERATION_SRCCOPY_AND);
252 nvc0_2d_inline(renderer->pushbuf, G80_2D_UNK0884, 0x3f);
253 nvc0_2d_inline(renderer->pushbuf, G80_2D_UNK0888, 1);
254
255 return true;
256
257error1:
258 nouveau_object_del(&renderer->nvc0_2d);
259error0:
260 return false;
261}
262
263static void
264nvc0_2d_finalize(struct nouveau_renderer *renderer)
265{
266 nouveau_object_del(&renderer->nvc0_2d);
267}
268
269struct wld_renderer *
270context_create_renderer(struct wld_context *base)
271{
272 struct nouveau_context *context = nouveau_context(base);
273 struct nouveau_renderer *renderer;
274 struct nvc0_fifo fifo = {};
275 int ret;
276
277 if (!(renderer = malloc(sizeof *renderer)))
278 goto error0;
279
280 ret = nouveau_object_new(&context->device->object, 0,
281 NOUVEAU_FIFO_CHANNEL_CLASS, &fifo, sizeof fifo,
282 &renderer->channel);
283
284 if (ret != 0)
285 goto error1;
286
287 ret = nouveau_pushbuf_new(context->client, renderer->channel, 4, 32 * 1024,
288 true, &renderer->pushbuf);
289
290 if (ret != 0)
291 goto error2;
292
293 if (nouveau_bufctx_new(context->client, 1, &renderer->bufctx) != 0)
294 goto error3;
295
296 if (!nvc0_2d_initialize(renderer))
297 goto error4;
298
299 renderer_initialize(&renderer->base, &wld_renderer_impl);
300 renderer->target = NULL;
301
302 return &renderer->base;
303
304error4:
305 nouveau_bufctx_del(&renderer->bufctx);
306error3:
307 nouveau_pushbuf_del(&renderer->pushbuf);
308error2:
309 nouveau_object_del(&renderer->channel);
310error1:
311 free(renderer);
312error0:
313 return NULL;
314}
315
316static bool export(struct wld_exporter *exporter, struct wld_buffer *base,
317 uint32_t type, union wld_object *object)
318{
319 struct nouveau_buffer *buffer = nouveau_buffer(base);
320
321 switch (type) {
322 case WLD_DRM_OBJECT_HANDLE:
323 object->u32 = buffer->bo->handle;
324 return true;
325 case WLD_DRM_OBJECT_PRIME_FD:
326 if (nouveau_bo_set_prime(buffer->bo, &object->i) != 0)
327 return false;
328 return true;
329 default:
330 return false;
331 }
332}
333
334static struct nouveau_buffer *
335new_buffer(struct nouveau_context *context,
336 uint32_t width, uint32_t height,
337 uint32_t format, uint32_t pitch)
338{
339 struct nouveau_buffer *buffer;
340
341 if (!(buffer = malloc(sizeof *buffer)))
342 return NULL;
343
344 buffer_initialize(&buffer->base, &wld_buffer_impl,
345 width, height, format, pitch);
346 buffer->context = context;
347 buffer->exporter.export = &export;
348 wld_buffer_add_exporter(&buffer->base.base, &buffer->exporter);
349
350 return buffer;
351}
352
353static inline uint32_t
354roundup(uint32_t value, uint32_t alignment)
355{
356 return (value + alignment - 1) & ~(alignment - 1);
357}
358
359struct buffer *
360context_create_buffer(struct wld_context *base,
361 uint32_t width, uint32_t height,
362 uint32_t format, uint32_t flags)
363{
364 struct nouveau_context *context = nouveau_context(base);
365 struct nouveau_buffer *buffer;
366 uint32_t bpp = format_bytes_per_pixel(format),
367 pitch = roundup(width * bpp, 64), bo_flags;
368 union nouveau_bo_config config = {};
369
370 if (!(buffer = new_buffer(context, width, height, format, pitch)))
371 goto error0;
372
373 bo_flags = NOUVEAU_BO_VRAM;
374
375 if (flags & WLD_DRM_FLAG_SCANOUT)
376 bo_flags |= NOUVEAU_BO_CONTIG;
377
378 if (height > 0x40 && !(flags & WLD_FLAG_MAP)) {
379 config.nvc0.tile_mode = 0x40;
380 config.nvc0.memtype = 0xfe;
381 height = roundup(height, 0x80);
382 } else
383 bo_flags |= NOUVEAU_BO_MAP;
384
385 if (nouveau_bo_new(context->device, bo_flags, 0, pitch * height,
386 &config, &buffer->bo)
387 != 0) {
388 goto error1;
389 }
390
391 return &buffer->base;
392
393error1:
394 free(buffer);
395error0:
396 return NULL;
397}
398
399struct buffer *
400context_import_buffer(struct wld_context *base,
401 uint32_t type, union wld_object object,
402 uint32_t width, uint32_t height,
403 uint32_t format, uint32_t pitch)
404{
405 struct nouveau_context *context = (void *)base;
406 struct nouveau_buffer *buffer;
407 struct nouveau_bo *bo = NULL;
408
409 switch (type) {
410 case WLD_DRM_OBJECT_PRIME_FD:
411 if (nouveau_bo_prime_handle_ref(context->device,
412 object.i, &bo)
413 != 0) {
414 goto error0;
415 }
416 break;
417 default:
418 goto error0;
419 }
420
421 if (!(buffer = new_buffer(context, width, height, format, pitch)))
422 goto error1;
423
424 buffer->bo = bo;
425
426 return &buffer->base;
427
428error1:
429 nouveau_bo_ref(NULL, &buffer->bo);
430error0:
431 return NULL;
432}
433
434void
435context_destroy(struct wld_context *base)
436{
437 struct nouveau_context *context = nouveau_context(base);
438
439 nouveau_client_del(&context->client);
440 nouveau_device_del(&context->device);
441 free(context);
442}
443
444/**** Renderer ****/
445uint32_t
446renderer_capabilities(struct wld_renderer *renderer,
447 struct buffer *buffer)
448{
449 if (buffer->base.impl == &wld_buffer_impl)
450 return WLD_CAPABILITY_READ | WLD_CAPABILITY_WRITE;
451
452 return 0;
453}
454
455bool
456renderer_set_target(struct wld_renderer *base, struct buffer *buffer)
457{
458 struct nouveau_renderer *renderer = nouveau_renderer(base);
459
460 if (buffer && buffer->base.impl != &wld_buffer_impl)
461 return false;
462
463 renderer->target = buffer ? nouveau_buffer(&buffer->base) : NULL;
464
465 return true;
466}
467
468static inline void
469nvc0_2d_use_buffer(struct nouveau_renderer *renderer,
470 struct nouveau_buffer *buffer,
471 uint16_t format_method, uint16_t format)
472{
473 uint32_t access = format == G80_2D_SRC_FORMAT ? NOUVEAU_BO_RD
474 : NOUVEAU_BO_WR;
475
476 nvc0_2d_inline(renderer->pushbuf, format_method, format);
477
478 if (buffer->bo->config.nvc0.memtype) {
479 nvc0_2d(renderer->pushbuf, format_method + 0x04, 2,
480 0, buffer->bo->config.nvc0.tile_mode);
481 } else {
482 nvc0_2d_inline(renderer->pushbuf, format_method + 0x04, 1);
483 nvc0_2d(renderer->pushbuf, format_method + 0x14, 1,
484 buffer->base.base.pitch);
485 }
486
487 nvc0_2d(renderer->pushbuf, format_method + 0x18, 4,
488 buffer->base.base.width, buffer->base.base.height,
489 buffer->bo->offset >> 32, buffer->bo->offset);
490 nouveau_bufctx_refn(renderer->bufctx, 0, buffer->bo,
491 NOUVEAU_BO_VRAM | access);
492}
493
494void
495renderer_fill_rectangle(struct wld_renderer *base, uint32_t color,
496 int32_t x, int32_t y,
497 uint32_t width, uint32_t height)
498{
499 struct nouveau_renderer *renderer = nouveau_renderer(base);
500 struct nouveau_buffer *dst = renderer->target;
501 uint32_t format;
502
503 if (!ensure_space(renderer->pushbuf, 18))
504 return;
505
506 format = nvc0_format(dst->base.base.format);
507
508 nouveau_bufctx_reset(renderer->bufctx, 0);
509 nvc0_2d_use_buffer(renderer, dst, G80_2D_DST_FORMAT, format);
510 nvc0_2d(renderer->pushbuf, G80_2D_DRAW_SHAPE, 3,
511 G80_2D_DRAW_SHAPE_RECTANGLES, format, color);
512 nouveau_pushbuf_bufctx(renderer->pushbuf, renderer->bufctx);
513
514 if (nouveau_pushbuf_validate(renderer->pushbuf) != 0)
515 return;
516
517 nvc0_2d(renderer->pushbuf, G80_2D_DRAW_POINT32_X(0), 4,
518 x, y, x + width, y + height);
519}
520
521void
522renderer_copy_rectangle(struct wld_renderer *base,
523 struct buffer *buffer_base,
524 int32_t dst_x, int32_t dst_y,
525 int32_t src_x, int32_t src_y,
526 uint32_t width, uint32_t height)
527{
528 struct nouveau_renderer *renderer = nouveau_renderer(base);
529
530 if (buffer_base->base.impl != &wld_buffer_impl)
531 return;
532
533 struct nouveau_buffer *src = nouveau_buffer(&buffer_base->base),
534 *dst = renderer->target;
535 uint32_t src_format, dst_format;
536
537 if (!ensure_space(renderer->pushbuf, 33))
538 return;
539
540 src_format = nvc0_format(src->base.base.format);
541 dst_format = nvc0_format(dst->base.base.format);
542
543 nouveau_bufctx_reset(renderer->bufctx, 0);
544 nvc0_2d_use_buffer(renderer, src, G80_2D_SRC_FORMAT, src_format);
545 nvc0_2d_use_buffer(renderer, dst, G80_2D_DST_FORMAT, dst_format);
546 nouveau_pushbuf_bufctx(renderer->pushbuf, renderer->bufctx);
547
548 if (nouveau_pushbuf_validate(renderer->pushbuf) != 0)
549 return;
550
551 nvc0_2d_inline(renderer->pushbuf, G80_GRAPH_SERIALIZE, 0);
552 nvc0_2d_inline(renderer->pushbuf, G80_2D_BLIT_CONTROL,
553 G80_2D_BLIT_CONTROL_ORIGIN_CENTER
554 | G80_2D_BLIT_CONTROL_FILTER_POINT_SAMPLE);
555 nvc0_2d(renderer->pushbuf, G80_2D_BLIT_DST_X, 12,
556 dst_x, dst_y, width, height, 0, 1, 0, 1, 0, src_x, 0, src_y);
557
558 renderer_flush(base);
559}
560
561void
562renderer_draw_text(struct wld_renderer *base,
563 struct font *font, uint32_t color,
564 int32_t x, int32_t y, const char *text,
565 uint32_t length, struct wld_extents *extents)
566{
567 struct nouveau_renderer *renderer = nouveau_renderer(base);
568 struct nouveau_buffer *dst = renderer->target;
569 uint32_t format;
570 int ret;
571 struct glyph *glyph;
572 FT_UInt glyph_index;
573 uint32_t c, count;
574 int32_t origin_x = x;
575
576 if (!ensure_space(renderer->pushbuf, 17))
577 return;
578
579 format = nvc0_format(dst->base.base.format);
580
581 nouveau_bufctx_reset(renderer->bufctx, 0);
582 nvc0_2d_use_buffer(renderer, dst, G80_2D_DST_FORMAT, format);
583 nvc0_2d_inline(renderer->pushbuf, G80_2D_SIFC_BITMAP_ENABLE, 1);
584 nvc0_2d(renderer->pushbuf, G80_2D_SIFC_BITMAP_FORMAT, 6,
585 G80_2D_SIFC_BITMAP_FORMAT_I1,
586 0, /* SIFC_FORMAT */
587 G80_2D_SIFC_BITMAP_LINE_PACK_MODE_ALIGN_BYTE,
588 0, color, /* SIFC_BITMAP_COLOR_BIT0, SIFC_BITMAP_COLOR_BIT1 */
589 0 /* SIFC_BITMAP_WRITE_BIT0_ENABLE */
590 );
591 nouveau_pushbuf_bufctx(renderer->pushbuf, renderer->bufctx);
592
593 if (nouveau_pushbuf_validate(renderer->pushbuf) != 0)
594 return;
595
596 if (length == -1)
597 length = strlen(text);
598
599 while ((ret = FcUtf8ToUcs4((FcChar8 *)text, &c, length)) > 0 && c != '\0') {
600 text += ret;
601 length -= ret;
602 glyph_index = FT_Get_Char_Index(font->face, c);
603
604 if (!font_ensure_glyph(font, glyph_index))
605 continue;
606
607 glyph = font->glyphs[glyph_index];
608
609 if (glyph->bitmap.width == 0 || glyph->bitmap.rows == 0)
610 goto advance;
611
612 if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
613 count = (glyph->bitmap.pitch * glyph->bitmap.rows + 3) / 4;
614 } else {
615 uint32_t bytes_per_row = (glyph->bitmap.width + 7) / 8;
616 count = (bytes_per_row * glyph->bitmap.rows + 3) / 4;
617 }
618
619 if (!ensure_space(renderer->pushbuf, 12 + count))
620 return;
621
622 if (glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) {
623 nvc0_2d(renderer->pushbuf, G80_2D_SIFC_WIDTH, 10,
624 /* Use the pitch instead of width to ensure the correct
625 * alignment is used. */
626 glyph->bitmap.pitch * 8, glyph->bitmap.rows,
627 0, 1, 0, 1,
628 0, origin_x + glyph->x, 0, y + glyph->y);
629 nv_add_dword(renderer->pushbuf,
630 nvc0_command(GF100_COMMAND_TYPE_NON_INCREASING,
631 GF100_SUBCHANNEL_2D,
632 G80_2D_SIFC_DATA, count));
633 nv_add_data(renderer->pushbuf, glyph->bitmap.buffer, count);
634 } else {
635 uint32_t bytes_per_row = (glyph->bitmap.width + 7) / 8;
636 uint32_t row;
637 uint8_t *mono = malloc(bytes_per_row * glyph->bitmap.rows);
638 uint8_t *dst = mono;
639
640 if (!mono)
641 return;
642
643 for (row = 0; row < glyph->bitmap.rows; ++row) {
644 const uint8_t *src = glyph->bitmap.buffer +
645 (row * glyph->bitmap.pitch);
646 pack_gray_row_to_mono(dst, src, glyph->bitmap.width);
647 dst += bytes_per_row;
648 }
649
650 nvc0_2d(renderer->pushbuf, G80_2D_SIFC_WIDTH, 10,
651 bytes_per_row * 8, glyph->bitmap.rows,
652 0, 1, 0, 1,
653 0, origin_x + glyph->x, 0, y + glyph->y);
654 nv_add_dword(renderer->pushbuf,
655 nvc0_command(GF100_COMMAND_TYPE_NON_INCREASING,
656 GF100_SUBCHANNEL_2D,
657 G80_2D_SIFC_DATA, count));
658 nv_add_data(renderer->pushbuf, mono, count);
659 free(mono);
660 }
661
662 advance:
663 origin_x += glyph->advance;
664 }
665
666 if (extents)
667 extents->advance = origin_x - x;
668}
669
670void
671renderer_flush(struct wld_renderer *base)
672{
673 struct nouveau_renderer *renderer = nouveau_renderer(base);
674
675 nouveau_pushbuf_kick(renderer->pushbuf, renderer->channel);
676 nouveau_pushbuf_bufctx(renderer->pushbuf, NULL);
677}
678
679void
680renderer_destroy(struct wld_renderer *base)
681{
682 struct nouveau_renderer *renderer = nouveau_renderer(base);
683
684 nvc0_2d_finalize(renderer);
685 nouveau_bufctx_del(&renderer->bufctx);
686 nouveau_pushbuf_del(&renderer->pushbuf);
687 nouveau_object_del(&renderer->channel);
688 free(renderer);
689}
690
691/**** Buffer ****/
692bool
693buffer_map(struct buffer *base)
694{
695 struct nouveau_buffer *buffer = nouveau_buffer(&base->base);
696
697 /* If the buffer is tiled, it cannot be mapped into virtual memory in order
698 * to appear linear like intel can do with map_gtt. */
699 if (buffer->bo->config.nvc0.tile_mode)
700 return false;
701
702 if (nouveau_bo_map(buffer->bo, NOUVEAU_BO_WR,
703 buffer->context->client)
704 != 0) {
705 return false;
706 }
707
708 buffer->base.base.map = buffer->bo->map;
709
710 return true;
711}
712
713bool
714buffer_unmap(struct buffer *base)
715{
716 struct nouveau_buffer *buffer = nouveau_buffer(&base->base);
717
718 if (munmap(buffer->bo->map, buffer->bo->size) == -1)
719 return false;
720
721 buffer->bo->map = NULL;
722 base->base.map = NULL;
723
724 return true;
725}
726
727void
728buffer_destroy(struct buffer *base)
729{
730 struct nouveau_buffer *buffer = nouveau_buffer(&base->base);
731
732 nouveau_bo_ref(NULL, &buffer->bo);
733 free(buffer);
734}