(hopefully) Fix build on powerpc.
[nouveau] / src / 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 #define READ_GET(ch) ((*(ch)->get - (ch)->dma.base) >> 2)
32 #define WRITE_PUT(ch, val) do {                       \
33         volatile int dum;                             \
34         NOUVEAU_DMA_BARRIER;                          \
35         dum=READ_GET(ch);                             \
36         *(ch)->put = (((val) << 2) + (ch)->dma.base); \
37         NOUVEAU_DMA_BARRIER;                          \
38 } while(0)
39
40 #ifdef NOUVEAU_DMA_DEBUG
41 char faulty[1024];
42 #endif
43
44 void
45 nouveau_dma_channel_init(struct nouveau_channel *userchan)
46 {
47         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
48         int i;
49
50         chan->dma.base = chan->drm.put_base;
51         chan->dma.cur  = chan->dma.put = RING_SKIPS;
52         chan->dma.max  = (chan->drm.cmdbuf_size >> 2) - 2;
53         chan->dma.free = chan->dma.max - chan->dma.cur;
54
55         for (i = 0; i < RING_SKIPS; i++)
56                 chan->pushbuf[i] = 0x00000000;
57 }
58 /* Reduce the cost of a very short wait, we're still burning cpu though. */
59 /* Also take the odd case into account that t_start is really 0. */
60 #define CHECK_TIMEOUT() do {                                                                            \
61         if (counter < 0xFFFFFFFF) {                                                                             \
62                 counter++;                                                                                              \
63         } else if (!t_start) {                                                                                          \
64                 t_start = NOUVEAU_TIME_MSEC();                                                          \
65                 if (!t_start)                                                                                           \
66                         t_start++;                                                                                      \
67         } else if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT) {     \
68                 return - EBUSY;                                                                                 \
69         }                                                                                                                       \
70 } while(0)
71
72 int
73 nouveau_dma_wait(struct nouveau_channel *userchan, int size)
74 {
75         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
76         uint32_t get, counter = 0, t_start = 0;
77
78         FIRE_RING_CH(userchan);
79
80         while (chan->dma.free < size) {
81                 get = READ_GET(chan);
82
83                 if (chan->dma.put >= get) {
84                         chan->dma.free = chan->dma.max - chan->dma.cur;
85
86                         if (chan->dma.free < size) {
87 #ifdef NOUVEAU_DMA_DEBUG
88                                 chan->dma.push_free = 1;
89 #endif
90                                 OUT_RING_CH(userchan,
91                                             0x20000000 | chan->dma.base);
92                                 if (get <= RING_SKIPS) {
93                                         /*corner case - will be idle*/
94                                         if (chan->dma.put <= RING_SKIPS)
95                                                 WRITE_PUT(chan, RING_SKIPS + 1);
96
97                                         do {
98                                                 CHECK_TIMEOUT();
99                                                 get = READ_GET(chan);
100                                         } while (get <= RING_SKIPS);
101                                 }
102
103                                 WRITE_PUT(chan, RING_SKIPS);
104                                 chan->dma.cur  = chan->dma.put = RING_SKIPS;
105                                 chan->dma.free = get - (RING_SKIPS + 1);
106                         }
107                 } else {
108                         chan->dma.free = get - chan->dma.cur - 1;
109                 }
110
111                 CHECK_TIMEOUT();
112         }
113
114         return 0;
115 }
116
117 #ifdef NOUVEAU_DMA_SUBCHAN_LRU
118 void
119 nouveau_dma_subc_bind(struct nouveau_grobj *grobj)
120 {
121         struct nouveau_channel_priv *chan = nouveau_channel(grobj->channel);
122         int subc = -1, i;
123         
124         for (i = 0; i < 8; i++) {
125                 if (chan->subchannel[i].grobj &&
126                     chan->subchannel[i].grobj->bound == 
127                     NOUVEAU_GROBJ_EXPLICIT_BIND)
128                         continue;
129                 /* Reading into the -1 index gives undefined results, so pick straight away. */
130                 if (subc == -1 || chan->subchannel[i].seq < chan->subchannel[subc].seq)
131                         subc = i;
132         }
133         assert(subc >= 0);
134
135         if (chan->subchannel[subc].grobj)
136                 chan->subchannel[subc].grobj->bound = 0;
137         chan->subchannel[subc].grobj = grobj;
138         grobj->subc  = subc;
139         grobj->bound = NOUVEAU_GROBJ_BOUND;
140
141         BEGIN_RING_CH(grobj->channel, grobj, 0, 1);
142         nouveau_dma_out  (grobj->channel, grobj->handle);
143 }
144 #endif
145
146 void
147 nouveau_dma_kickoff(struct nouveau_channel *userchan)
148 {
149         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
150         uint32_t put_offset;
151         int i;
152
153         if (chan->dma.cur == chan->dma.put)
154                 return;
155
156         if (chan->num_relocs) {
157                 nouveau_bo_validate(userchan);
158                 
159                 for (i = 0; i < chan->num_relocs; i++) {
160                         struct nouveau_bo_reloc *r = &chan->relocs[i];
161                         uint32_t push;
162
163                         if (r->flags & NOUVEAU_BO_LOW) {
164                                 push = r->bo->base.offset + r->data;
165                         } else
166                         if (r->flags & NOUVEAU_BO_HIGH) {
167                                 push = (r->bo->base.offset + r->data) >> 32;
168                         } else {
169                                 push = r->data;
170                         }
171
172                         if (r->flags & NOUVEAU_BO_OR) {
173                                 if (r->flags & NOUVEAU_BO_VRAM)
174                                         push |= r->vor;
175                                 else
176                                         push |= r->tor;
177                         }
178
179                         *r->ptr = push;
180                 }
181
182                 chan->num_relocs = 0;
183         }
184
185 #ifdef NOUVEAU_DMA_DEBUG
186         if (chan->dma.push_free) {
187                 NOUVEAU_ERR("Packet incomplete: %d left\n", chan->dma.push_free);
188                 return;
189         }
190 #endif
191
192         put_offset = (chan->dma.cur << 2) + chan->dma.base;
193 #ifdef NOUVEAU_DMA_TRACE
194         NOUVEAU_MSG("FIRE_RING %d/0x%08x\n", chan->drm.channel, put_offset);
195 #endif
196         chan->dma.put  = chan->dma.cur;
197         NOUVEAU_DMA_BARRIER;
198         *chan->put     = put_offset;
199         NOUVEAU_DMA_BARRIER;
200 }