aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_dma.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_dma.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_dma.c108
1 files changed, 73 insertions, 35 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
index 703553687b20..50d9e67745af 100644
--- a/drivers/gpu/drm/nouveau/nouveau_dma.c
+++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
@@ -29,12 +29,22 @@
29#include "nouveau_drv.h" 29#include "nouveau_drv.h"
30#include "nouveau_dma.h" 30#include "nouveau_dma.h"
31 31
32void
33nouveau_dma_pre_init(struct nouveau_channel *chan)
34{
35 chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
36 chan->dma.put = 0;
37 chan->dma.cur = chan->dma.put;
38 chan->dma.free = chan->dma.max - chan->dma.cur;
39}
40
32int 41int
33nouveau_dma_init(struct nouveau_channel *chan) 42nouveau_dma_init(struct nouveau_channel *chan)
34{ 43{
35 struct drm_device *dev = chan->dev; 44 struct drm_device *dev = chan->dev;
36 struct drm_nouveau_private *dev_priv = dev->dev_private; 45 struct drm_nouveau_private *dev_priv = dev->dev_private;
37 struct nouveau_gpuobj *m2mf = NULL; 46 struct nouveau_gpuobj *m2mf = NULL;
47 struct nouveau_gpuobj *nvsw = NULL;
38 int ret, i; 48 int ret, i;
39 49
40 /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */ 50 /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
@@ -47,6 +57,15 @@ nouveau_dma_init(struct nouveau_channel *chan)
47 if (ret) 57 if (ret)
48 return ret; 58 return ret;
49 59
60 /* Create an NV_SW object for various sync purposes */
61 ret = nouveau_gpuobj_sw_new(chan, NV_SW, &nvsw);
62 if (ret)
63 return ret;
64
65 ret = nouveau_gpuobj_ref_add(dev, chan, NvSw, nvsw, NULL);
66 if (ret)
67 return ret;
68
50 /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */ 69 /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
51 ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy); 70 ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
52 if (ret) 71 if (ret)
@@ -64,12 +83,6 @@ nouveau_dma_init(struct nouveau_channel *chan)
64 return ret; 83 return ret;
65 } 84 }
66 85
67 /* Initialise DMA vars */
68 chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
69 chan->dma.put = 0;
70 chan->dma.cur = chan->dma.put;
71 chan->dma.free = chan->dma.max - chan->dma.cur;
72
73 /* Insert NOPS for NOUVEAU_DMA_SKIPS */ 86 /* Insert NOPS for NOUVEAU_DMA_SKIPS */
74 ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); 87 ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
75 if (ret) 88 if (ret)
@@ -87,6 +100,13 @@ nouveau_dma_init(struct nouveau_channel *chan)
87 BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1); 100 BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
88 OUT_RING(chan, NvNotify0); 101 OUT_RING(chan, NvNotify0);
89 102
103 /* Initialise NV_SW */
104 ret = RING_SPACE(chan, 2);
105 if (ret)
106 return ret;
107 BEGIN_RING(chan, NvSubSw, 0, 1);
108 OUT_RING(chan, NvSw);
109
90 /* Sit back and pray the channel works.. */ 110 /* Sit back and pray the channel works.. */
91 FIRE_RING(chan); 111 FIRE_RING(chan);
92 112
@@ -106,47 +126,52 @@ OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
106 chan->dma.cur += nr_dwords; 126 chan->dma.cur += nr_dwords;
107} 127}
108 128
109static inline bool 129/* Fetch and adjust GPU GET pointer
110READ_GET(struct nouveau_channel *chan, uint32_t *get) 130 *
131 * Returns:
132 * value >= 0, the adjusted GET pointer
133 * -EINVAL if GET pointer currently outside main push buffer
134 * -EBUSY if timeout exceeded
135 */
136static inline int
137READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)
111{ 138{
112 uint32_t val; 139 uint32_t val;
113 140
114 val = nvchan_rd32(chan, chan->user_get); 141 val = nvchan_rd32(chan, chan->user_get);
115 if (val < chan->pushbuf_base || 142
116 val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) { 143 /* reset counter as long as GET is still advancing, this is
117 /* meaningless to dma_wait() except to know whether the 144 * to avoid misdetecting a GPU lockup if the GPU happens to
118 * GPU has stalled or not 145 * just be processing an operation that takes a long time
119 */ 146 */
120 *get = val; 147 if (val != *prev_get) {
121 return false; 148 *prev_get = val;
149 *timeout = 0;
150 }
151
152 if ((++*timeout & 0xff) == 0) {
153 DRM_UDELAY(1);
154 if (*timeout > 100000)
155 return -EBUSY;
122 } 156 }
123 157
124 *get = (val - chan->pushbuf_base) >> 2; 158 if (val < chan->pushbuf_base ||
125 return true; 159 val > chan->pushbuf_base + (chan->dma.max << 2))
160 return -EINVAL;
161
162 return (val - chan->pushbuf_base) >> 2;
126} 163}
127 164
128int 165int
129nouveau_dma_wait(struct nouveau_channel *chan, int size) 166nouveau_dma_wait(struct nouveau_channel *chan, int size)
130{ 167{
131 uint32_t get, prev_get = 0, cnt = 0; 168 uint32_t prev_get = 0, cnt = 0;
132 bool get_valid; 169 int get;
133 170
134 while (chan->dma.free < size) { 171 while (chan->dma.free < size) {
135 /* reset counter as long as GET is still advancing, this is 172 get = READ_GET(chan, &prev_get, &cnt);
136 * to avoid misdetecting a GPU lockup if the GPU happens to 173 if (unlikely(get == -EBUSY))
137 * just be processing an operation that takes a long time 174 return -EBUSY;
138 */
139 get_valid = READ_GET(chan, &get);
140 if (get != prev_get) {
141 prev_get = get;
142 cnt = 0;
143 }
144
145 if ((++cnt & 0xff) == 0) {
146 DRM_UDELAY(1);
147 if (cnt > 100000)
148 return -EBUSY;
149 }
150 175
151 /* loop until we have a usable GET pointer. the value 176 /* loop until we have a usable GET pointer. the value
152 * we read from the GPU may be outside the main ring if 177 * we read from the GPU may be outside the main ring if
@@ -157,7 +182,7 @@ nouveau_dma_wait(struct nouveau_channel *chan, int size)
157 * from the SKIPS area, so the code below doesn't have to deal 182 * from the SKIPS area, so the code below doesn't have to deal
158 * with some fun corner cases. 183 * with some fun corner cases.
159 */ 184 */
160 if (!get_valid || get < NOUVEAU_DMA_SKIPS) 185 if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS)
161 continue; 186 continue;
162 187
163 if (get <= chan->dma.cur) { 188 if (get <= chan->dma.cur) {
@@ -183,6 +208,19 @@ nouveau_dma_wait(struct nouveau_channel *chan, int size)
183 * after processing the currently pending commands. 208 * after processing the currently pending commands.
184 */ 209 */
185 OUT_RING(chan, chan->pushbuf_base | 0x20000000); 210 OUT_RING(chan, chan->pushbuf_base | 0x20000000);
211
212 /* wait for GET to depart from the skips area.
213 * prevents writing GET==PUT and causing a race
214 * condition that causes us to think the GPU is
215 * idle when it's not.
216 */
217 do {
218 get = READ_GET(chan, &prev_get, &cnt);
219 if (unlikely(get == -EBUSY))
220 return -EBUSY;
221 if (unlikely(get == -EINVAL))
222 continue;
223 } while (get <= NOUVEAU_DMA_SKIPS);
186 WRITE_PUT(NOUVEAU_DMA_SKIPS); 224 WRITE_PUT(NOUVEAU_DMA_SKIPS);
187 225
188 /* we're now submitting commands at the start of 226 /* we're now submitting commands at the start of