1/*
2 * Copyright © 2009 Red Hat, Inc.
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 (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 *
23 * Authors:
24 *    Dave Airlie <airlied@redhat.com>
25 *
26 */
27#ifdef HAVE_CONFIG_H
28#include "config.h"
29#endif
30
31#include <errno.h>
32#include "radeon.h"
33#include "radeon_bo.h"
34#include "radeon_cs.h"
35
36#define VBO_SIZE (16*1024)
37
38/* KMS vertex buffer support - for R600 only but could be used on previous gpus */
39
40static struct radeon_bo *radeon_vbo_get_bo(ScrnInfoPtr pScrn);
41
42void radeon_vbo_put(ScrnInfoPtr pScrn, struct radeon_vbo_object *vbo)
43{
44
45    if (vbo->vb_bo) {
46	radeon_bo_unmap(vbo->vb_bo);
47	radeon_bo_unref(vbo->vb_bo);
48	vbo->vb_bo = NULL;
49	vbo->vb_total = 0;
50    }
51
52    vbo->vb_offset = 0;
53}
54
55void radeon_vbo_get(ScrnInfoPtr pScrn, struct radeon_vbo_object *vbo)
56{
57    int ret;
58
59    vbo->vb_bo = radeon_vbo_get_bo(pScrn);
60    if (vbo->vb_bo) {
61	radeon_bo_ref(vbo->vb_bo);
62	ret = radeon_bo_map(vbo->vb_bo, 1);
63	if (ret)
64	    FatalError("Failed to map vb %d\n", ret);
65    }
66
67    vbo->vb_total = VBO_SIZE;
68    vbo->vb_offset = 0;
69    vbo->vb_start_op = vbo->vb_offset;
70}
71
72/* these functions could migrate to libdrm and
73   be shared with the radeon 3D driver */
74static int radeon_bo_is_idle(struct radeon_bo *bo)
75{
76    uint32_t domain;
77    int ret = radeon_bo_is_busy(bo, &domain);
78    return ret != -EBUSY;
79}
80
81void radeon_vbo_init_lists(ScrnInfoPtr pScrn)
82{
83    RADEONInfoPtr info = RADEONPTR(pScrn);
84    struct radeon_accel_state *accel_state = info->accel_state;
85
86    accel_state->use_vbos = TRUE;
87    make_empty_list(&accel_state->bo_free);
88    make_empty_list(&accel_state->bo_wait);
89    make_empty_list(&accel_state->bo_reserved);
90}
91
92void radeon_vbo_free_lists(ScrnInfoPtr pScrn)
93{
94    RADEONInfoPtr info = RADEONPTR(pScrn);
95    struct radeon_accel_state *accel_state = info->accel_state;
96    struct radeon_dma_bo *dma_bo, *temp;
97
98    foreach_s(dma_bo, temp, &accel_state->bo_free) {
99	remove_from_list(dma_bo);
100	radeon_bo_unref(dma_bo->bo);
101	free(dma_bo);
102    }
103
104    foreach_s(dma_bo, temp, &accel_state->bo_wait) {
105	remove_from_list(dma_bo);
106	radeon_bo_unref(dma_bo->bo);
107	free(dma_bo);
108    }
109
110    foreach_s(dma_bo, temp, &accel_state->bo_reserved) {
111	remove_from_list(dma_bo);
112	radeon_bo_unref(dma_bo->bo);
113	free(dma_bo);
114    }
115}
116
117void radeon_vbo_flush_bos(ScrnInfoPtr pScrn)
118{
119    RADEONInfoPtr info = RADEONPTR(pScrn);
120    struct radeon_accel_state *accel_state = info->accel_state;
121    struct radeon_dma_bo *dma_bo, *temp;
122    const int expire_at = ++accel_state->bo_free.expire_counter + DMA_BO_FREE_TIME;
123    const int time = accel_state->bo_free.expire_counter;
124
125    foreach_s(dma_bo, temp, &accel_state->bo_wait) {
126	if (dma_bo->expire_counter == time) {
127	    ErrorF("leaking dma buffer\n");
128	    while ((dma_bo->bo = radeon_bo_unref(dma_bo->bo))) {}
129	    remove_from_list(dma_bo);
130	    free(dma_bo);
131	    continue;
132	}
133
134	if (!radeon_bo_is_idle(dma_bo->bo))
135	    continue;
136
137	if (dma_bo->bo->ptr) {
138	    ErrorF("bo with pointer on wait list!\n");
139	    continue;
140	}
141
142	remove_from_list(dma_bo);
143	dma_bo->expire_counter = expire_at;
144	insert_at_tail(&accel_state->bo_free, dma_bo);
145    }
146
147    /* move reserved to wait list */
148    foreach_s(dma_bo, temp, &accel_state->bo_reserved) {
149	remove_from_list(dma_bo);
150	dma_bo->expire_counter = expire_at;
151	insert_at_tail(&accel_state->bo_wait, dma_bo);
152    }
153
154    /* free bos that have been unused */
155    foreach_s(dma_bo, temp, &accel_state->bo_free) {
156	if (dma_bo->expire_counter != time)
157	    break;
158	/* always keep one hanging around at end */
159	if (at_end(&accel_state->bo_free, dma_bo)) {
160	    dma_bo->expire_counter = time + DMA_BO_FREE_TIME;
161	    break;
162	}
163
164	remove_from_list(dma_bo);
165	radeon_bo_unref(dma_bo->bo);
166	free(dma_bo);
167    }
168}
169
170static struct radeon_bo *radeon_vbo_get_bo(ScrnInfoPtr pScrn)
171{
172    RADEONInfoPtr info = RADEONPTR(pScrn);
173    struct radeon_accel_state *accel_state = info->accel_state;
174    struct radeon_dma_bo *dma_bo = NULL;
175    struct radeon_bo *bo;
176
177    if (is_empty_list(&accel_state->bo_free)) {
178	dma_bo = calloc(1, sizeof(struct radeon_dma_bo));
179	if (!dma_bo)
180	    return NULL;
181
182again_alloc:
183	dma_bo->bo = radeon_bo_open(info->bufmgr, 0, VBO_SIZE,
184				    0, RADEON_GEM_DOMAIN_GTT, 0);
185
186	if (!dma_bo->bo) {
187	    ErrorF("failure to allocate DMA BO\n");
188	    free(dma_bo);
189	    return NULL;
190	}
191	insert_at_head(&accel_state->bo_reserved, dma_bo);
192    } else {
193	dma_bo = last_elem(&accel_state->bo_free);
194	remove_from_list(dma_bo);
195	insert_at_head(&accel_state->bo_reserved, dma_bo);
196    }
197
198    if (is_empty_list(&accel_state->bo_reserved))
199	goto again_alloc;
200
201    bo = first_elem(&accel_state->bo_reserved)->bo;
202
203    /* need a space check */
204    if (radeon_cs_space_check_with_bo(info->cs,
205				      bo,
206				      RADEON_GEM_DOMAIN_GTT, 0))
207	ErrorF("failed to revalidate\n");
208
209    return bo;
210}
211
212