Use bios PLL functions on nv4x
[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
59 #define CHECK_TIMEOUT() do {                                                   \
60         if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT)             \
61                 return - EBUSY;                                                \
62 } while(0)
63
64 int
65 nouveau_dma_wait(struct nouveau_channel *userchan, int size)
66 {
67         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
68         uint32_t get, t_start;
69
70         FIRE_RING_CH(userchan);
71
72         t_start = NOUVEAU_TIME_MSEC();
73         while (chan->dma.free < size) {
74                 get = READ_GET(chan);
75
76                 if (chan->dma.put >= get) {
77                         chan->dma.free = chan->dma.max - chan->dma.cur;
78
79                         if (chan->dma.free < size) {
80 #ifdef NOUVEAU_DMA_DEBUG
81                                 chan->dma.push_free = 1;
82 #endif
83                                 OUT_RING_CH(userchan,
84                                             0x20000000 | chan->dma.base);
85                                 if (get <= RING_SKIPS) {
86                                         /*corner case - will be idle*/
87                                         if (chan->dma.put <= RING_SKIPS)
88                                                 WRITE_PUT(chan, RING_SKIPS + 1);
89
90                                         do {
91                                                 CHECK_TIMEOUT();
92                                                 get = READ_GET(chan);
93                                         } while (get <= RING_SKIPS);
94                                 }
95
96                                 WRITE_PUT(chan, RING_SKIPS);
97                                 chan->dma.cur  = chan->dma.put = RING_SKIPS;
98                                 chan->dma.free = get - (RING_SKIPS + 1);
99                         }
100                 } else {
101                         chan->dma.free = get - chan->dma.cur - 1;
102                 }
103
104                 CHECK_TIMEOUT();
105         }
106
107         return 0;
108 }
109
110 #ifdef NOUVEAU_DMA_SUBCHAN_LRU
111 void
112 nouveau_dma_subc_bind(struct nouveau_grobj *grobj)
113 {
114         struct nouveau_channel_priv *chan = nouveau_channel(grobj->channel);
115         int subc = -1, i;
116         
117         for (i = 0; i < 8; i++) {
118                 if (chan->subchannel[i].grobj &&
119                     chan->subchannel[i].grobj->bound == 
120                     NOUVEAU_GROBJ_EXPLICIT_BIND)
121                         continue;
122                 /* Reading into the -1 index gives undefined results, so pick straight away. */
123                 if (subc == -1 || chan->subchannel[i].seq < chan->subchannel[subc].seq)
124                         subc = i;
125         }
126         assert(subc >= 0);
127
128         if (chan->subchannel[subc].grobj)
129                 chan->subchannel[subc].grobj->bound = 0;
130         chan->subchannel[subc].grobj = grobj;
131         grobj->subc  = subc;
132         grobj->bound = NOUVEAU_GROBJ_BOUND;
133
134         BEGIN_RING_CH(grobj->channel, grobj, 0, 1);
135         nouveau_dma_out  (grobj->channel, grobj->handle);
136 }
137 #endif
138
139 void
140 nouveau_dma_kickoff(struct nouveau_channel *userchan)
141 {
142         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
143         uint32_t put_offset;
144         int i;
145
146         if (chan->dma.cur == chan->dma.put)
147                 return;
148
149         if (chan->num_relocs) {
150                 nouveau_bo_validate(userchan);
151                 
152                 for (i = 0; i < chan->num_relocs; i++) {
153                         struct nouveau_bo_reloc *r = &chan->relocs[i];
154                         uint32_t push;
155
156                         if (r->flags & NOUVEAU_BO_LOW) {
157                                 push = r->bo->base.offset + r->data;
158                         } else
159                         if (r->flags & NOUVEAU_BO_HIGH) {
160                                 push = (r->bo->base.offset + r->data) >> 32;
161                         } else {
162                                 push = r->data;
163                         }
164
165                         if (r->flags & NOUVEAU_BO_OR) {
166                                 if (r->flags & NOUVEAU_BO_VRAM)
167                                         push |= r->vor;
168                                 else
169                                         push |= r->tor;
170                         }
171
172                         *r->ptr = push;
173                 }
174
175                 chan->num_relocs = 0;
176         }
177
178 #ifdef NOUVEAU_DMA_DEBUG
179         if (chan->dma.push_free) {
180                 NOUVEAU_ERR("Packet incomplete: %d left\n", chan->dma.push_free);
181                 return;
182         }
183 #endif
184
185         put_offset = (chan->dma.cur << 2) + chan->dma.base;
186 #ifdef NOUVEAU_DMA_TRACE
187         NOUVEAU_MSG("FIRE_RING %d/0x%08x\n", chan->drm.channel, put_offset);
188 #endif
189         chan->dma.put  = chan->dma.cur;
190         NOUVEAU_DMA_BARRIER;
191         *chan->put     = put_offset;
192         NOUVEAU_DMA_BARRIER;
193 }