1 /* 2 * Copyright (C) 2007 Ben Skeggs. 3 * All Rights Reserved. 4 * 5 * Permission is hereby granted, free of charge, to any person obtaining 6 * a copy of this software and associated documentation files (the 7 * "Software"), to deal in the Software without restriction, including 8 * without limitation the rights to use, copy, modify, merge, publish, 9 * distribute, sublicense, and/or sell copies of the Software, and to 10 * permit persons to whom the Software is furnished to do so, subject to 11 * the following conditions: 12 * 13 * The above copyright notice and this permission notice (including the 14 * next paragraph) shall be included in all copies or substantial 15 * portions of the Software. 16 * 17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE 21 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 22 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 23 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 * 25 */ 26 27 #include "drmP.h" 28 #include "drm.h" 29 #include "nouveau_drv.h" 30 #include "nouveau_dma.h" 31 32 int 33 nouveau_dma_init(struct nouveau_channel *chan) 34 { 35 struct drm_device *dev = chan->dev; 36 struct drm_nouveau_private *dev_priv = dev->dev_private; 37 struct nouveau_gpuobj *m2mf = NULL; 38 int ret, i; 39 40 /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */ 41 ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ? 42 0x0039 : 0x5039, &m2mf); 43 if (ret) 44 return ret; 45 46 ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL); 47 if (ret) 48 return ret; 49 50 /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */ 51 ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy); 52 if (ret) 53 return ret; 54 55 /* Map push buffer */ 56 ret = nouveau_bo_map(chan->pushbuf_bo); 57 if (ret) 58 return ret; 59 60 /* Map M2MF notifier object - fbcon. */ 61 if (drm_core_check_feature(dev, DRIVER_MODESET)) { 62 ret = nouveau_bo_map(chan->notifier_bo); 63 if (ret) 64 return ret; 65 } 66 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 */ 74 ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS); 75 if (ret) 76 return ret; 77 78 for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) 79 OUT_RING(chan, 0); 80 81 /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */ 82 ret = RING_SPACE(chan, 4); 83 if (ret) 84 return ret; 85 BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1); 86 OUT_RING(chan, NvM2MF); 87 BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1); 88 OUT_RING(chan, NvNotify0); 89 90 /* Sit back and pray the channel works.. */ 91 FIRE_RING(chan); 92 93 return 0; 94 } 95 96 void 97 OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords) 98 { 99 bool is_iomem; 100 u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem); 101 mem = &mem[chan->dma.cur]; 102 if (is_iomem) 103 memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4); 104 else 105 memcpy(mem, data, nr_dwords * 4); 106 chan->dma.cur += nr_dwords; 107 } 108 109 static inline bool 110 READ_GET(struct nouveau_channel *chan, uint32_t *get) 111 { 112 uint32_t val; 113 114 val = nvchan_rd32(chan, chan->user_get); 115 if (val < chan->pushbuf_base || 116 val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) { 117 /* meaningless to dma_wait() except to know whether the 118 * GPU has stalled or not 119 */ 120 *get = val; 121 return false; 122 } 123 124 *get = (val - chan->pushbuf_base) >> 2; 125 return true; 126 } 127 128 int 129 nouveau_dma_wait(struct nouveau_channel *chan, int size) 130 { 131 uint32_t get, prev_get = 0, cnt = 0; 132 bool get_valid; 133 134 while (chan->dma.free < size) { 135 /* reset counter as long as GET is still advancing, this is 136 * to avoid misdetecting a GPU lockup if the GPU happens to 137 * just be processing an operation that takes a long time 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 151 /* loop until we have a usable GET pointer. the value 152 * we read from the GPU may be outside the main ring if 153 * PFIFO is processing a buffer called from the main ring, 154 * discard these values until something sensible is seen. 155 * 156 * the other case we discard GET is while the GPU is fetching 157 * from the SKIPS area, so the code below doesn't have to deal 158 * with some fun corner cases. 159 */ 160 if (!get_valid || get < NOUVEAU_DMA_SKIPS) 161 continue; 162 163 if (get <= chan->dma.cur) { 164 /* engine is fetching behind us, or is completely 165 * idle (GET == PUT) so we have free space up until 166 * the end of the push buffer 167 * 168 * we can only hit that path once per call due to 169 * looping back to the beginning of the push buffer, 170 * we'll hit the fetching-ahead-of-us path from that 171 * point on. 172 * 173 * the *one* exception to that rule is if we read 174 * GET==PUT, in which case the below conditional will 175 * always succeed and break us out of the wait loop. 176 */ 177 chan->dma.free = chan->dma.max - chan->dma.cur; 178 if (chan->dma.free >= size) 179 break; 180 181 /* not enough space left at the end of the push buffer, 182 * instruct the GPU to jump back to the start right 183 * after processing the currently pending commands. 184 */ 185 OUT_RING(chan, chan->pushbuf_base | 0x20000000); 186 WRITE_PUT(NOUVEAU_DMA_SKIPS); 187 188 /* we're now submitting commands at the start of 189 * the push buffer. 190 */ 191 chan->dma.cur = 192 chan->dma.put = NOUVEAU_DMA_SKIPS; 193 } 194 195 /* engine fetching ahead of us, we have space up until the 196 * current GET pointer. the "- 1" is to ensure there's 197 * space left to emit a jump back to the beginning of the 198 * push buffer if we require it. we can never get GET == PUT 199 * here, so this is safe. 200 */ 201 chan->dma.free = get - chan->dma.cur - 1; 202 } 203 204 return 0; 205 } 206 207