OSDN Git Service

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