2 * Copyright 2007 Nouveau Project
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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
27 #include "nouveau_drmif.h"
28 #include "nouveau_dma.h"
29 #include "nouveau_local.h"
31 #define READ_GET(ch) ((*(ch)->get - (ch)->dma.base) >> 2)
32 #define WRITE_PUT(ch, val) do { \
34 NOUVEAU_DMA_BARRIER; \
36 *(ch)->put = (((val) << 2) + (ch)->dma.base); \
37 NOUVEAU_DMA_BARRIER; \
40 #ifdef NOUVEAU_DMA_DEBUG
45 nouveau_dma_channel_init(struct nouveau_channel *userchan)
47 struct nouveau_channel_priv *chan = nouveau_channel(userchan);
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;
55 for (i = 0; i < RING_SKIPS; i++)
56 chan->pushbuf[i] = 0x00000000;
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) { \
63 } else if (!t_start) { \
64 t_start = NOUVEAU_TIME_MSEC(); \
67 } else if ((NOUVEAU_TIME_MSEC() - t_start) > NOUVEAU_DMA_TIMEOUT) { \
73 nouveau_dma_wait(struct nouveau_channel *userchan, int size)
75 struct nouveau_channel_priv *chan = nouveau_channel(userchan);
76 uint32_t get, counter = 0, t_start = 0;
78 FIRE_RING_CH(userchan);
80 while (chan->dma.free < size) {
83 if (chan->dma.put >= get) {
84 chan->dma.free = chan->dma.max - chan->dma.cur;
86 if (chan->dma.free < size) {
87 #ifdef NOUVEAU_DMA_DEBUG
88 chan->dma.push_free = 1;
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);
100 } while (get <= RING_SKIPS);
103 WRITE_PUT(chan, RING_SKIPS);
104 chan->dma.cur = chan->dma.put = RING_SKIPS;
105 chan->dma.free = get - (RING_SKIPS + 1);
108 chan->dma.free = get - chan->dma.cur - 1;
117 #ifdef NOUVEAU_DMA_SUBCHAN_LRU
119 nouveau_dma_subc_bind(struct nouveau_grobj *grobj)
121 struct nouveau_channel_priv *chan = nouveau_channel(grobj->channel);
124 for (i = 0; i < 8; i++) {
125 if (chan->subchannel[i].grobj &&
126 chan->subchannel[i].grobj->bound ==
127 NOUVEAU_GROBJ_EXPLICIT_BIND)
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)
135 if (chan->subchannel[subc].grobj)
136 chan->subchannel[subc].grobj->bound = 0;
137 chan->subchannel[subc].grobj = grobj;
139 grobj->bound = NOUVEAU_GROBJ_BOUND;
141 BEGIN_RING_CH(grobj->channel, grobj, 0, 1);
142 nouveau_dma_out (grobj->channel, grobj->handle);
147 nouveau_dma_kickoff(struct nouveau_channel *userchan)
149 struct nouveau_channel_priv *chan = nouveau_channel(userchan);
153 if (chan->dma.cur == chan->dma.put)
156 if (chan->num_relocs) {
157 nouveau_bo_validate(userchan);
159 for (i = 0; i < chan->num_relocs; i++) {
160 struct nouveau_bo_reloc *r = &chan->relocs[i];
163 if (r->flags & NOUVEAU_BO_LOW) {
164 push = r->bo->base.offset + r->data;
166 if (r->flags & NOUVEAU_BO_HIGH) {
167 push = (r->bo->base.offset + r->data) >> 32;
172 if (r->flags & NOUVEAU_BO_OR) {
173 if (r->flags & NOUVEAU_BO_VRAM)
182 chan->num_relocs = 0;
185 #ifdef NOUVEAU_DMA_DEBUG
186 if (chan->dma.push_free) {
187 NOUVEAU_ERR("Packet incomplete: %d left\n", chan->dma.push_free);
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);
196 chan->dma.put = chan->dma.cur;
198 *chan->put = put_offset;