randr12: Improve crosswiring for digital outputs + minor changes and cleanup.
[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                 if (chan->subchannel[i].seq < chan->subchannel[subc].seq)
123                         subc = i;
124         }
125         assert(subc >= 0);
126
127         if (chan->subchannel[subc].grobj)
128                 chan->subchannel[subc].grobj->bound = 0;
129         chan->subchannel[subc].grobj = grobj;
130         grobj->subc  = subc;
131         grobj->bound = NOUVEAU_GROBJ_BOUND;
132
133         BEGIN_RING_CH(grobj->channel, grobj, 0, 1);
134         nouveau_dma_out  (grobj->channel, grobj->handle);
135 }
136 #endif
137
138 void
139 nouveau_dma_kickoff(struct nouveau_channel *userchan)
140 {
141         struct nouveau_channel_priv *chan = nouveau_channel(userchan);
142         uint32_t put_offset;
143         int i;
144
145         if (chan->dma.cur == chan->dma.put)
146                 return;
147
148         if (chan->num_relocs) {
149                 nouveau_bo_validate(userchan);
150                 
151                 for (i = 0; i < chan->num_relocs; i++) {
152                         struct nouveau_bo_reloc *r = &chan->relocs[i];
153                         uint32_t push;
154
155                         if (r->flags & NOUVEAU_BO_LOW) {
156                                 push = r->bo->base.offset + r->data;
157                         } else
158                         if (r->flags & NOUVEAU_BO_HIGH) {
159                                 push = (r->bo->base.offset + r->data) >> 32;
160                         } else {
161                                 push = r->data;
162                         }
163
164                         if (r->flags & NOUVEAU_BO_OR) {
165                                 if (r->flags & NOUVEAU_BO_VRAM)
166                                         push |= r->vor;
167                                 else
168                                         push |= r->tor;
169                         }
170
171                         *r->ptr = push;
172                 }
173
174                 chan->num_relocs = 0;
175         }
176
177 #ifdef NOUVEAU_DMA_DEBUG
178         if (chan->dma.push_free) {
179                 NOUVEAU_ERR("Packet incomplete: %d left\n", chan->dma.push_free);
180                 return;
181         }
182 #endif
183
184         put_offset = (chan->dma.cur << 2) + chan->dma.base;
185 #ifdef NOUVEAU_DMA_TRACE
186         NOUVEAU_MSG("FIRE_RING %d/0x%08x\n", chan->drm.channel, put_offset);
187 #endif
188         chan->dma.put  = chan->dma.cur;
189         NOUVEAU_DMA_BARRIER;
190         *chan->put     = put_offset;
191         NOUVEAU_DMA_BARRIER;
192 }