1 1.5 riastrad /* $NetBSD: nouveau_dma.c,v 1.5 2021/12/18 23:45:32 riastradh Exp $ */ 2 1.2 riastrad 3 1.1 riastrad /* 4 1.1 riastrad * Copyright (C) 2007 Ben Skeggs. 5 1.1 riastrad * All Rights Reserved. 6 1.1 riastrad * 7 1.1 riastrad * Permission is hereby granted, free of charge, to any person obtaining 8 1.1 riastrad * a copy of this software and associated documentation files (the 9 1.1 riastrad * "Software"), to deal in the Software without restriction, including 10 1.1 riastrad * without limitation the rights to use, copy, modify, merge, publish, 11 1.1 riastrad * distribute, sublicense, and/or sell copies of the Software, and to 12 1.1 riastrad * permit persons to whom the Software is furnished to do so, subject to 13 1.1 riastrad * the following conditions: 14 1.1 riastrad * 15 1.1 riastrad * The above copyright notice and this permission notice (including the 16 1.1 riastrad * next paragraph) shall be included in all copies or substantial 17 1.1 riastrad * portions of the Software. 18 1.1 riastrad * 19 1.1 riastrad * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 1.1 riastrad * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 1.1 riastrad * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 22 1.1 riastrad * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 23 1.1 riastrad * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 1.1 riastrad * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 1.1 riastrad * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 1.1 riastrad * 27 1.1 riastrad */ 28 1.1 riastrad 29 1.2 riastrad #include <sys/cdefs.h> 30 1.3 riastrad __KERNEL_RCSID(0, "$NetBSD: nouveau_dma.c,v 1.5 2021/12/18 23:45:32 riastradh Exp $"); 31 1.1 riastrad 32 1.5 riastrad #include "nouveau_drv.h" 33 1.1 riastrad #include "nouveau_dma.h" 34 1.5 riastrad #include "nouveau_vmm.h" 35 1.5 riastrad 36 1.5 riastrad #include <nvif/user.h> 37 1.1 riastrad 38 1.2 riastrad #ifdef __NetBSD__ 39 1.2 riastrad # define __iomem 40 1.2 riastrad # define __force 41 1.2 riastrad #endif 42 1.2 riastrad 43 1.1 riastrad void 44 1.1 riastrad OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords) 45 1.1 riastrad { 46 1.1 riastrad bool is_iomem; 47 1.1 riastrad u32 *mem = ttm_kmap_obj_virtual(&chan->push.buffer->kmap, &is_iomem); 48 1.1 riastrad mem = &mem[chan->dma.cur]; 49 1.1 riastrad if (is_iomem) 50 1.1 riastrad memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4); 51 1.1 riastrad else 52 1.1 riastrad memcpy(mem, data, nr_dwords * 4); 53 1.1 riastrad chan->dma.cur += nr_dwords; 54 1.1 riastrad } 55 1.1 riastrad 56 1.2 riastrad #ifdef __NetBSD__ 57 1.2 riastrad # undef __force 58 1.2 riastrad # undef __iomem 59 1.2 riastrad #endif 60 1.2 riastrad 61 1.1 riastrad /* Fetch and adjust GPU GET pointer 62 1.1 riastrad * 63 1.1 riastrad * Returns: 64 1.1 riastrad * value >= 0, the adjusted GET pointer 65 1.1 riastrad * -EINVAL if GET pointer currently outside main push buffer 66 1.1 riastrad * -EBUSY if timeout exceeded 67 1.1 riastrad */ 68 1.1 riastrad static inline int 69 1.1 riastrad READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout) 70 1.1 riastrad { 71 1.1 riastrad uint64_t val; 72 1.1 riastrad 73 1.3 riastrad val = nvif_rd32(&chan->user, chan->user_get); 74 1.1 riastrad if (chan->user_get_hi) 75 1.3 riastrad val |= (uint64_t)nvif_rd32(&chan->user, chan->user_get_hi) << 32; 76 1.1 riastrad 77 1.1 riastrad /* reset counter as long as GET is still advancing, this is 78 1.1 riastrad * to avoid misdetecting a GPU lockup if the GPU happens to 79 1.1 riastrad * just be processing an operation that takes a long time 80 1.1 riastrad */ 81 1.1 riastrad if (val != *prev_get) { 82 1.1 riastrad *prev_get = val; 83 1.1 riastrad *timeout = 0; 84 1.1 riastrad } 85 1.1 riastrad 86 1.1 riastrad if ((++*timeout & 0xff) == 0) { 87 1.1 riastrad udelay(1); 88 1.1 riastrad if (*timeout > 100000) 89 1.1 riastrad return -EBUSY; 90 1.1 riastrad } 91 1.1 riastrad 92 1.5 riastrad if (val < chan->push.addr || 93 1.5 riastrad val > chan->push.addr + (chan->dma.max << 2)) 94 1.1 riastrad return -EINVAL; 95 1.1 riastrad 96 1.5 riastrad return (val - chan->push.addr) >> 2; 97 1.1 riastrad } 98 1.1 riastrad 99 1.1 riastrad void 100 1.5 riastrad nv50_dma_push(struct nouveau_channel *chan, u64 offset, int length) 101 1.1 riastrad { 102 1.5 riastrad struct nvif_user *user = &chan->drm->client.device.user; 103 1.1 riastrad struct nouveau_bo *pb = chan->push.buffer; 104 1.1 riastrad int ip = (chan->dma.ib_put * 2) + chan->dma.ib_base; 105 1.1 riastrad 106 1.1 riastrad BUG_ON(chan->dma.ib_free < 1); 107 1.1 riastrad 108 1.1 riastrad nouveau_bo_wr32(pb, ip++, lower_32_bits(offset)); 109 1.1 riastrad nouveau_bo_wr32(pb, ip++, upper_32_bits(offset) | length << 8); 110 1.1 riastrad 111 1.1 riastrad chan->dma.ib_put = (chan->dma.ib_put + 1) & chan->dma.ib_max; 112 1.1 riastrad 113 1.1 riastrad mb(); 114 1.1 riastrad /* Flush writes. */ 115 1.1 riastrad nouveau_bo_rd32(pb, 0); 116 1.1 riastrad 117 1.3 riastrad nvif_wr32(&chan->user, 0x8c, chan->dma.ib_put); 118 1.5 riastrad if (user->func && user->func->doorbell) 119 1.5 riastrad user->func->doorbell(user, chan->token); 120 1.1 riastrad chan->dma.ib_free--; 121 1.1 riastrad } 122 1.1 riastrad 123 1.1 riastrad static int 124 1.1 riastrad nv50_dma_push_wait(struct nouveau_channel *chan, int count) 125 1.1 riastrad { 126 1.1 riastrad uint32_t cnt = 0, prev_get = 0; 127 1.1 riastrad 128 1.1 riastrad while (chan->dma.ib_free < count) { 129 1.3 riastrad uint32_t get = nvif_rd32(&chan->user, 0x88); 130 1.1 riastrad if (get != prev_get) { 131 1.1 riastrad prev_get = get; 132 1.1 riastrad cnt = 0; 133 1.1 riastrad } 134 1.1 riastrad 135 1.1 riastrad if ((++cnt & 0xff) == 0) { 136 1.5 riastrad udelay(1); 137 1.1 riastrad if (cnt > 100000) 138 1.1 riastrad return -EBUSY; 139 1.1 riastrad } 140 1.1 riastrad 141 1.1 riastrad chan->dma.ib_free = get - chan->dma.ib_put; 142 1.1 riastrad if (chan->dma.ib_free <= 0) 143 1.1 riastrad chan->dma.ib_free += chan->dma.ib_max; 144 1.1 riastrad } 145 1.1 riastrad 146 1.1 riastrad return 0; 147 1.1 riastrad } 148 1.1 riastrad 149 1.1 riastrad static int 150 1.1 riastrad nv50_dma_wait(struct nouveau_channel *chan, int slots, int count) 151 1.1 riastrad { 152 1.1 riastrad uint64_t prev_get = 0; 153 1.1 riastrad int ret, cnt = 0; 154 1.1 riastrad 155 1.1 riastrad ret = nv50_dma_push_wait(chan, slots + 1); 156 1.1 riastrad if (unlikely(ret)) 157 1.1 riastrad return ret; 158 1.1 riastrad 159 1.1 riastrad while (chan->dma.free < count) { 160 1.1 riastrad int get = READ_GET(chan, &prev_get, &cnt); 161 1.1 riastrad if (unlikely(get < 0)) { 162 1.1 riastrad if (get == -EINVAL) 163 1.1 riastrad continue; 164 1.1 riastrad 165 1.1 riastrad return get; 166 1.1 riastrad } 167 1.1 riastrad 168 1.1 riastrad if (get <= chan->dma.cur) { 169 1.1 riastrad chan->dma.free = chan->dma.max - chan->dma.cur; 170 1.1 riastrad if (chan->dma.free >= count) 171 1.1 riastrad break; 172 1.1 riastrad 173 1.1 riastrad FIRE_RING(chan); 174 1.1 riastrad do { 175 1.1 riastrad get = READ_GET(chan, &prev_get, &cnt); 176 1.1 riastrad if (unlikely(get < 0)) { 177 1.1 riastrad if (get == -EINVAL) 178 1.1 riastrad continue; 179 1.1 riastrad return get; 180 1.1 riastrad } 181 1.1 riastrad } while (get == 0); 182 1.1 riastrad chan->dma.cur = 0; 183 1.1 riastrad chan->dma.put = 0; 184 1.1 riastrad } 185 1.1 riastrad 186 1.1 riastrad chan->dma.free = get - chan->dma.cur - 1; 187 1.1 riastrad } 188 1.1 riastrad 189 1.1 riastrad return 0; 190 1.1 riastrad } 191 1.1 riastrad 192 1.1 riastrad int 193 1.1 riastrad nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size) 194 1.1 riastrad { 195 1.1 riastrad uint64_t prev_get = 0; 196 1.1 riastrad int cnt = 0, get; 197 1.1 riastrad 198 1.1 riastrad if (chan->dma.ib_max) 199 1.1 riastrad return nv50_dma_wait(chan, slots, size); 200 1.1 riastrad 201 1.1 riastrad while (chan->dma.free < size) { 202 1.1 riastrad get = READ_GET(chan, &prev_get, &cnt); 203 1.1 riastrad if (unlikely(get == -EBUSY)) 204 1.1 riastrad return -EBUSY; 205 1.1 riastrad 206 1.1 riastrad /* loop until we have a usable GET pointer. the value 207 1.1 riastrad * we read from the GPU may be outside the main ring if 208 1.1 riastrad * PFIFO is processing a buffer called from the main ring, 209 1.1 riastrad * discard these values until something sensible is seen. 210 1.1 riastrad * 211 1.1 riastrad * the other case we discard GET is while the GPU is fetching 212 1.1 riastrad * from the SKIPS area, so the code below doesn't have to deal 213 1.1 riastrad * with some fun corner cases. 214 1.1 riastrad */ 215 1.1 riastrad if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS) 216 1.1 riastrad continue; 217 1.1 riastrad 218 1.1 riastrad if (get <= chan->dma.cur) { 219 1.1 riastrad /* engine is fetching behind us, or is completely 220 1.1 riastrad * idle (GET == PUT) so we have free space up until 221 1.1 riastrad * the end of the push buffer 222 1.1 riastrad * 223 1.1 riastrad * we can only hit that path once per call due to 224 1.1 riastrad * looping back to the beginning of the push buffer, 225 1.1 riastrad * we'll hit the fetching-ahead-of-us path from that 226 1.1 riastrad * point on. 227 1.1 riastrad * 228 1.1 riastrad * the *one* exception to that rule is if we read 229 1.1 riastrad * GET==PUT, in which case the below conditional will 230 1.1 riastrad * always succeed and break us out of the wait loop. 231 1.1 riastrad */ 232 1.1 riastrad chan->dma.free = chan->dma.max - chan->dma.cur; 233 1.1 riastrad if (chan->dma.free >= size) 234 1.1 riastrad break; 235 1.1 riastrad 236 1.1 riastrad /* not enough space left at the end of the push buffer, 237 1.1 riastrad * instruct the GPU to jump back to the start right 238 1.1 riastrad * after processing the currently pending commands. 239 1.1 riastrad */ 240 1.5 riastrad OUT_RING(chan, chan->push.addr | 0x20000000); 241 1.1 riastrad 242 1.1 riastrad /* wait for GET to depart from the skips area. 243 1.1 riastrad * prevents writing GET==PUT and causing a race 244 1.1 riastrad * condition that causes us to think the GPU is 245 1.1 riastrad * idle when it's not. 246 1.1 riastrad */ 247 1.1 riastrad do { 248 1.1 riastrad get = READ_GET(chan, &prev_get, &cnt); 249 1.1 riastrad if (unlikely(get == -EBUSY)) 250 1.1 riastrad return -EBUSY; 251 1.1 riastrad if (unlikely(get == -EINVAL)) 252 1.1 riastrad continue; 253 1.1 riastrad } while (get <= NOUVEAU_DMA_SKIPS); 254 1.1 riastrad WRITE_PUT(NOUVEAU_DMA_SKIPS); 255 1.1 riastrad 256 1.1 riastrad /* we're now submitting commands at the start of 257 1.1 riastrad * the push buffer. 258 1.1 riastrad */ 259 1.1 riastrad chan->dma.cur = 260 1.1 riastrad chan->dma.put = NOUVEAU_DMA_SKIPS; 261 1.1 riastrad } 262 1.1 riastrad 263 1.1 riastrad /* engine fetching ahead of us, we have space up until the 264 1.1 riastrad * current GET pointer. the "- 1" is to ensure there's 265 1.1 riastrad * space left to emit a jump back to the beginning of the 266 1.1 riastrad * push buffer if we require it. we can never get GET == PUT 267 1.1 riastrad * here, so this is safe. 268 1.1 riastrad */ 269 1.1 riastrad chan->dma.free = get - chan->dma.cur - 1; 270 1.1 riastrad } 271 1.1 riastrad 272 1.1 riastrad return 0; 273 1.1 riastrad } 274 1.1 riastrad 275