Merge branch 'upstream-gallium-0.1' into nouveau-gallium-0.1
[mesa.git] / src / mesa / drivers / dri / nouveau_winsys / nouveau_dma.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 <assert.h>
25 #include <errno.h>
26
27 #include "nouveau_drmif.h"
28 #include "nouveau_dma.h"
29 #include "nouveau_local.h"
30
31 static inline uint32_t
32 READ_GET(struct nouveau_channel_priv *nvchan)
33 {
34 return *nvchan->get;
35 }
36
37 static inline void
38 WRITE_PUT(struct nouveau_channel_priv *nvchan, uint32_t val)
39 {
40 uint32_t put = ((val << 2) + nvchan->dma->base);
41 volatile int dum;
42
43 NOUVEAU_DMA_BARRIER;
44 dum = READ_GET(nvchan);
45
46 *nvchan->put = put;
47 nvchan->dma->put = val;
48 #ifdef NOUVEAU_DMA_TRACE
49 NOUVEAU_MSG("WRITE_PUT %d/0x%08x\n", nvchan->drm.channel, put);
50 #endif
51
52 NOUVEAU_DMA_BARRIER;
53 }
54
55 static inline int
56 LOCAL_GET(struct nouveau_dma_priv *dma, uint32_t *val)
57 {
58 uint32_t get = *val;
59
60 if (get >= dma->base && get <= (dma->base + (dma->max << 2))) {
61 *val = (get - dma->base) >> 2;
62 return 1;
63 }
64
65 return 0;
66 }
67
68 void
69 nouveau_dma_channel_init(struct nouveau_channel *chan)
70 {
71 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
72 int i;
73
74 nvchan->dma = &nvchan->dma_master;
75 nvchan->dma->base = nvchan->drm.put_base;
76 nvchan->dma->cur = nvchan->dma->put = 0;
77 nvchan->dma->max = (nvchan->drm.cmdbuf_size >> 2) - 2;
78 nvchan->dma->free = nvchan->dma->max - nvchan->dma->cur;
79
80 RING_SPACE_CH(chan, RING_SKIPS);
81 for (i = 0; i < RING_SKIPS; i++)
82 OUT_RING_CH(chan, 0);
83 }
84
85 #define CHECK_TIMEOUT() do { \
86 if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT) \
87 return - EBUSY; \
88 } while(0)
89
90 int
91 nouveau_dma_wait(struct nouveau_channel *chan, int size)
92 {
93 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
94 struct nouveau_dma_priv *dma = nvchan->dma;
95 uint32_t get, t_start;
96
97 FIRE_RING_CH(chan);
98
99 t_start = NOUVEAU_TIME_MSEC();
100 while (dma->free < size) {
101 CHECK_TIMEOUT();
102
103 get = READ_GET(nvchan);
104 if (!LOCAL_GET(dma, &get))
105 continue;
106
107 if (dma->put >= get) {
108 dma->free = dma->max - dma->cur;
109
110 if (dma->free < size) {
111 #ifdef NOUVEAU_DMA_DEBUG
112 dma->push_free = 1;
113 #endif
114 OUT_RING_CH(chan, 0x20000000 | dma->base);
115 if (get <= RING_SKIPS) {
116 /*corner case - will be idle*/
117 if (dma->put <= RING_SKIPS)
118 WRITE_PUT(nvchan,
119 RING_SKIPS + 1);
120
121 do {
122 CHECK_TIMEOUT();
123 get = READ_GET(nvchan);
124 if (!LOCAL_GET(dma, &get))
125 get = 0;
126 } while (get <= RING_SKIPS);
127 }
128
129 WRITE_PUT(nvchan, RING_SKIPS);
130 dma->cur = dma->put = RING_SKIPS;
131 dma->free = get - (RING_SKIPS + 1);
132 }
133 } else {
134 dma->free = get - dma->cur - 1;
135 }
136 }
137
138 return 0;
139 }
140
141 #ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
142 static void
143 nouveau_dma_parse_pushbuf(struct nouveau_channel *chan, int get, int put)
144 {
145 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
146 unsigned mthd_count = 0;
147
148 while (get != put) {
149 uint32_t gpuget = (get << 2) + nvchan->drm.put_base;
150 uint32_t data;
151
152 if (get < 0 || get >= nvchan->drm.cmdbuf_size) {
153 NOUVEAU_ERR("DMA_PT 0x%08x\n", gpuget);
154 assert(0);
155 }
156 data = nvchan->pushbuf[get++];
157
158 if (mthd_count) {
159 NOUVEAU_MSG("0x%08x 0x%08x\n", gpuget, data);
160 mthd_count--;
161 continue;
162 }
163
164 switch (data & 0x60000000) {
165 case 0x00000000:
166 mthd_count = (data >> 18) & 0x7ff;
167 NOUVEAU_MSG("0x%08x 0x%08x MTHD "
168 "Sc %d Mthd 0x%04x Size %d\n",
169 gpuget, data, (data>>13) & 7, data & 0x1ffc,
170 mthd_count);
171 break;
172 case 0x20000000:
173 get = (data & 0x1ffffffc) >> 2;
174 NOUVEAU_MSG("0x%08x 0x%08x JUMP 0x%08x\n",
175 gpuget, data, data & 0x1ffffffc);
176 continue;
177 case 0x40000000:
178 mthd_count = (data >> 18) & 0x7ff;
179 NOUVEAU_MSG("0x%08x 0x%08x NINC "
180 "Sc %d Mthd 0x%04x Size %d\n",
181 gpuget, data, (data>>13) & 7, data & 0x1ffc,
182 mthd_count);
183 break;
184 case 0x60000000:
185 /* DMA_OPCODE_CALL apparently, doesn't seem to work on
186 * my NV40 at least..
187 */
188 /* fall-through */
189 default:
190 NOUVEAU_MSG("DMA_PUSHER 0x%08x 0x%08x\n",
191 gpuget, data);
192 assert(0);
193 }
194 }
195 }
196 #endif
197
198 void
199 nouveau_dma_kickoff(struct nouveau_channel *chan)
200 {
201 struct nouveau_channel_priv *nvchan = nouveau_channel(chan);
202 struct nouveau_dma_priv *dma = nvchan->dma;
203
204 if (dma->cur == dma->put)
205 return;
206
207 #ifdef NOUVEAU_DMA_DEBUG
208 if (dma->push_free) {
209 NOUVEAU_ERR("Packet incomplete: %d left\n", dma->push_free);
210 return;
211 }
212 #endif
213
214 #ifdef NOUVEAU_DMA_DUMP_POSTRELOC_PUSHBUF
215 nouveau_dma_parse_pushbuf(chan, dma->put, dma->cur);
216 #endif
217
218 WRITE_PUT(nvchan, dma->cur);
219 }