r600g: add support for s3tc formats.
[mesa.git] / src / gallium / drivers / nvc0 / nvc0_fence.c
1 /*
2 * Copyright 2010 Christoph Bumiller
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 "nvc0_fence.h"
24 #include "nvc0_context.h"
25 #include "nvc0_screen.h"
26
27 #ifdef PIPE_OS_UNIX
28 #include <sched.h>
29 #endif
30
31 boolean
32 nvc0_screen_fence_new(struct nvc0_screen *screen, struct nvc0_fence **fence,
33 boolean emit)
34 {
35 *fence = CALLOC_STRUCT(nvc0_fence);
36 if (!*fence)
37 return FALSE;
38
39 (*fence)->screen = screen;
40 (*fence)->ref = 1;
41
42 if (emit)
43 nvc0_fence_emit(*fence);
44
45 return TRUE;
46 }
47
48 void
49 nvc0_fence_emit(struct nvc0_fence *fence)
50 {
51 struct nvc0_screen *screen = fence->screen;
52 struct nouveau_channel *chan = screen->base.channel;
53
54 fence->sequence = ++screen->fence.sequence;
55
56 assert(fence->state == NVC0_FENCE_STATE_AVAILABLE);
57
58 MARK_RING (chan, 5, 2);
59 BEGIN_RING(chan, RING_3D(QUERY_ADDRESS_HIGH), 4);
60 OUT_RELOCh(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
61 OUT_RELOCl(chan, screen->fence.bo, 0, NOUVEAU_BO_WR);
62 OUT_RING (chan, fence->sequence);
63 OUT_RING (chan, NVC0_3D_QUERY_GET_FENCE | NVC0_3D_QUERY_GET_SHORT |
64 (0xf << NVC0_3D_QUERY_GET_UNIT__SHIFT));
65
66 ++fence->ref;
67
68 if (screen->fence.tail)
69 screen->fence.tail->next = fence;
70 else
71 screen->fence.head = fence;
72
73 screen->fence.tail = fence;
74
75 fence->state = NVC0_FENCE_STATE_EMITTED;
76 }
77
78 static void
79 nvc0_fence_trigger_release_buffers(struct nvc0_fence *fence);
80
81 void
82 nvc0_fence_del(struct nvc0_fence *fence)
83 {
84 struct nvc0_fence *it;
85 struct nvc0_screen *screen = fence->screen;
86
87 if (fence->state == NVC0_FENCE_STATE_EMITTED) {
88 if (fence == screen->fence.head) {
89 screen->fence.head = fence->next;
90 if (!screen->fence.head)
91 screen->fence.tail = NULL;
92 } else {
93 for (it = screen->fence.head; it && it->next != fence; it = it->next);
94 it->next = fence->next;
95 if (screen->fence.tail == fence)
96 screen->fence.tail = it;
97 }
98 }
99
100 if (fence->buffers) {
101 debug_printf("WARNING: deleting fence with buffers "
102 "still hooked to it !\n");
103 nvc0_fence_trigger_release_buffers(fence);
104 }
105
106 FREE(fence);
107 }
108
109 static void
110 nvc0_fence_trigger_release_buffers(struct nvc0_fence *fence)
111 {
112 struct nvc0_mm_allocation *alloc = fence->buffers;
113
114 while (alloc) {
115 struct nvc0_mm_allocation *next = alloc->next;
116 nvc0_mm_free(alloc);
117 alloc = next;
118 };
119 fence->buffers = NULL;
120 }
121
122 static void
123 nvc0_screen_fence_update(struct nvc0_screen *screen)
124 {
125 struct nvc0_fence *fence;
126 struct nvc0_fence *next = NULL;
127 uint32_t sequence = screen->fence.map[0];
128
129 if (screen->fence.sequence_ack == sequence)
130 return;
131 screen->fence.sequence_ack = sequence;
132
133 for (fence = screen->fence.head; fence; fence = next) {
134 next = fence->next;
135 sequence = fence->sequence;
136
137 fence->state = NVC0_FENCE_STATE_SIGNALLED;
138
139 if (fence->buffers)
140 nvc0_fence_trigger_release_buffers(fence);
141
142 nvc0_fence_reference(&fence, NULL);
143
144 if (sequence == screen->fence.sequence_ack)
145 break;
146 }
147 screen->fence.head = next;
148 if (!next)
149 screen->fence.tail = NULL;
150 }
151
152 #define NVC0_FENCE_MAX_SPINS (1 << 17)
153
154 boolean
155 nvc0_fence_signalled(struct nvc0_fence *fence)
156 {
157 struct nvc0_screen *screen = fence->screen;
158
159 if (fence->state == NVC0_FENCE_STATE_EMITTED)
160 nvc0_screen_fence_update(screen);
161
162 return fence->state == NVC0_FENCE_STATE_SIGNALLED;
163 }
164
165 boolean
166 nvc0_fence_wait(struct nvc0_fence *fence)
167 {
168 struct nvc0_screen *screen = fence->screen;
169 int spins = 0;
170
171 if (fence->state == NVC0_FENCE_STATE_AVAILABLE) {
172 nvc0_fence_emit(fence);
173
174 FIRE_RING(screen->base.channel);
175
176 if (fence == screen->fence.current)
177 nvc0_screen_fence_new(screen, &screen->fence.current, FALSE);
178 }
179
180 do {
181 nvc0_screen_fence_update(screen);
182
183 if (fence->state == NVC0_FENCE_STATE_SIGNALLED)
184 return TRUE;
185 spins++;
186 #ifdef PIPE_OS_UNIX
187 if (!(spins % 8)) /* donate a few cycles */
188 sched_yield();
189 #endif
190 } while (spins < NVC0_FENCE_MAX_SPINS);
191
192 if (spins > 9000)
193 NOUVEAU_ERR("fence %x: been spinning too long\n", fence->sequence);
194
195 return FALSE;
196 }
197
198 void
199 nvc0_screen_fence_next(struct nvc0_screen *screen)
200 {
201 nvc0_fence_emit(screen->fence.current);
202 nvc0_screen_fence_new(screen, &screen->fence.current, FALSE);
203 nvc0_screen_fence_update(screen);
204 }