1 | /* $NetBSD: nouveau_subdev_vm_nv44.c,v 1.3 2015/10/14 00:12:55 mrg Exp $ */ |
2 | |
3 | /* |
4 | * Copyright 2012 Red Hat Inc. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a |
7 | * copy of this software and associated documentation files (the "Software"), |
8 | * to deal in the Software without restriction, including without limitation |
9 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
10 | * and/or sell copies of the Software, and to permit persons to whom the |
11 | * Software is furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
20 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
21 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
22 | * OTHER DEALINGS IN THE SOFTWARE. |
23 | * |
24 | * Authors: Ben Skeggs |
25 | */ |
26 | |
27 | #include <sys/cdefs.h> |
28 | __KERNEL_RCSID(0, "$NetBSD: nouveau_subdev_vm_nv44.c,v 1.3 2015/10/14 00:12:55 mrg Exp $" ); |
29 | |
30 | #include <core/gpuobj.h> |
31 | #include <core/option.h> |
32 | |
33 | #include <subdev/timer.h> |
34 | #include <subdev/vm.h> |
35 | |
36 | #include "nv04.h" |
37 | |
38 | #define NV44_GART_SIZE (512 * 1024 * 1024) |
39 | #define NV44_GART_PAGE ( 4 * 1024) |
40 | |
41 | /******************************************************************************* |
42 | * VM map/unmap callbacks |
43 | ******************************************************************************/ |
44 | |
45 | static void |
46 | nv44_vm_fill(struct nouveau_gpuobj *pgt, dma_addr_t null, |
47 | dma_addr_t *list, u32 pte, u32 cnt) |
48 | { |
49 | u32 base = (pte << 2) & ~0x0000000f; |
50 | u32 tmp[4]; |
51 | |
52 | tmp[0] = nv_ro32(pgt, base + 0x0); |
53 | tmp[1] = nv_ro32(pgt, base + 0x4); |
54 | tmp[2] = nv_ro32(pgt, base + 0x8); |
55 | tmp[3] = nv_ro32(pgt, base + 0xc); |
56 | |
57 | while (cnt--) { |
58 | u32 addr = list ? (*list++ >> 12) : (null >> 12); |
59 | switch (pte++ & 0x3) { |
60 | case 0: |
61 | tmp[0] &= ~0x07ffffff; |
62 | tmp[0] |= addr; |
63 | break; |
64 | case 1: |
65 | tmp[0] &= ~0xf8000000; |
66 | tmp[0] |= addr << 27; |
67 | tmp[1] &= ~0x003fffff; |
68 | tmp[1] |= addr >> 5; |
69 | break; |
70 | case 2: |
71 | tmp[1] &= ~0xffc00000; |
72 | tmp[1] |= addr << 22; |
73 | tmp[2] &= ~0x0001ffff; |
74 | tmp[2] |= addr >> 10; |
75 | break; |
76 | case 3: |
77 | tmp[2] &= ~0xfffe0000; |
78 | tmp[2] |= addr << 17; |
79 | tmp[3] &= ~0x00000fff; |
80 | tmp[3] |= addr >> 15; |
81 | break; |
82 | } |
83 | } |
84 | |
85 | nv_wo32(pgt, base + 0x0, tmp[0]); |
86 | nv_wo32(pgt, base + 0x4, tmp[1]); |
87 | nv_wo32(pgt, base + 0x8, tmp[2]); |
88 | nv_wo32(pgt, base + 0xc, tmp[3] | 0x40000000); |
89 | } |
90 | |
91 | static void |
92 | nv44_vm_map_sg(struct nouveau_vma *vma, struct nouveau_gpuobj *pgt, |
93 | struct nouveau_mem *mem, u32 pte, u32 cnt, dma_addr_t *list) |
94 | { |
95 | struct nv04_vmmgr_priv *priv = (void *)vma->vm->vmm; |
96 | u32 tmp[4]; |
97 | int i; |
98 | |
99 | if (pte & 3) { |
100 | u32 max = 4 - (pte & 3); |
101 | u32 part = (cnt > max) ? max : cnt; |
102 | nv44_vm_fill(pgt, priv->null, list, pte, part); |
103 | pte += part; |
104 | list += part; |
105 | cnt -= part; |
106 | } |
107 | |
108 | while (cnt >= 4) { |
109 | for (i = 0; i < 4; i++) |
110 | tmp[i] = *list++ >> 12; |
111 | nv_wo32(pgt, pte++ * 4, tmp[0] >> 0 | tmp[1] << 27); |
112 | nv_wo32(pgt, pte++ * 4, tmp[1] >> 5 | tmp[2] << 22); |
113 | nv_wo32(pgt, pte++ * 4, tmp[2] >> 10 | tmp[3] << 17); |
114 | nv_wo32(pgt, pte++ * 4, tmp[3] >> 15 | 0x40000000); |
115 | cnt -= 4; |
116 | } |
117 | |
118 | if (cnt) |
119 | nv44_vm_fill(pgt, priv->null, list, pte, cnt); |
120 | } |
121 | |
122 | static void |
123 | nv44_vm_unmap(struct nouveau_gpuobj *pgt, u32 pte, u32 cnt) |
124 | { |
125 | struct nv04_vmmgr_priv *priv = (void *)nouveau_vmmgr(pgt); |
126 | |
127 | if (pte & 3) { |
128 | u32 max = 4 - (pte & 3); |
129 | u32 part = (cnt > max) ? max : cnt; |
130 | nv44_vm_fill(pgt, priv->null, NULL, pte, part); |
131 | pte += part; |
132 | cnt -= part; |
133 | } |
134 | |
135 | while (cnt >= 4) { |
136 | nv_wo32(pgt, pte++ * 4, 0x00000000); |
137 | nv_wo32(pgt, pte++ * 4, 0x00000000); |
138 | nv_wo32(pgt, pte++ * 4, 0x00000000); |
139 | nv_wo32(pgt, pte++ * 4, 0x00000000); |
140 | cnt -= 4; |
141 | } |
142 | |
143 | if (cnt) |
144 | nv44_vm_fill(pgt, priv->null, NULL, pte, cnt); |
145 | } |
146 | |
147 | static void |
148 | nv44_vm_flush(struct nouveau_vm *vm) |
149 | { |
150 | struct nv04_vmmgr_priv *priv = (void *)vm->vmm; |
151 | nv_wr32(priv, 0x100814, priv->base.limit - NV44_GART_PAGE); |
152 | nv_wr32(priv, 0x100808, 0x00000020); |
153 | if (!nv_wait(priv, 0x100808, 0x00000001, 0x00000001)) |
154 | nv_error(priv, "timeout: 0x%08x\n" , nv_rd32(priv, 0x100808)); |
155 | nv_wr32(priv, 0x100808, 0x00000000); |
156 | } |
157 | |
158 | /******************************************************************************* |
159 | * VMMGR subdev |
160 | ******************************************************************************/ |
161 | |
162 | static int |
163 | nv44_vmmgr_ctor(struct nouveau_object *parent, struct nouveau_object *engine, |
164 | struct nouveau_oclass *oclass, void *data, u32 size, |
165 | struct nouveau_object **pobject) |
166 | { |
167 | struct nouveau_device *device = nv_device(parent); |
168 | struct nv04_vmmgr_priv *priv; |
169 | int ret; |
170 | |
171 | if (pci_find_capability(device->pdev, PCI_CAP_ID_AGP) || |
172 | !nouveau_boolopt(device->cfgopt, "NvPCIE" , true)) { |
173 | return nouveau_object_ctor(parent, engine, &nv04_vmmgr_oclass, |
174 | data, size, pobject); |
175 | } |
176 | |
177 | ret = nouveau_vmmgr_create(parent, engine, oclass, "PCIEGART" , |
178 | "pciegart" , &priv); |
179 | *pobject = nv_object(priv); |
180 | if (ret) |
181 | return ret; |
182 | |
183 | priv->base.create = nv04_vm_create; |
184 | priv->base.limit = NV44_GART_SIZE; |
185 | priv->base.dma_bits = 39; |
186 | priv->base.pgt_bits = 32 - 12; |
187 | priv->base.spg_shift = 12; |
188 | priv->base.lpg_shift = 12; |
189 | priv->base.map_sg = nv44_vm_map_sg; |
190 | priv->base.unmap = nv44_vm_unmap; |
191 | priv->base.flush = nv44_vm_flush; |
192 | |
193 | #ifdef __NetBSD__ |
194 | { |
195 | const bus_dma_tag_t dmat = pci_dma64_available(&device->pdev->pd_pa) ? |
196 | device->pdev->pd_pa.pa_dmat64 : device->pdev->pd_pa.pa_dmat; |
197 | int nsegs; |
198 | |
199 | /* XXX errno NetBSD->Linux */ |
200 | ret = -bus_dmamem_alloc(dmat, PAGE_SIZE, PAGE_SIZE, 0, |
201 | &priv->nullseg, 1, &nsegs, BUS_DMA_WAITOK); |
202 | if (ret) { |
203 | /* XXX Need to destroy stuff... */ |
204 | fail0: nv_error(priv, "unable to allocate dummy pages\n" ); |
205 | return ret; |
206 | } |
207 | KASSERT(nsegs == 1); |
208 | |
209 | /* XXX errno NetBSD->Linux */ |
210 | ret = -bus_dmamap_create(dmat, PAGE_SIZE, 1, PAGE_SIZE, 0, |
211 | BUS_DMA_WAITOK, &priv->nullmap); |
212 | if (ret) { |
213 | fail1: bus_dmamem_free(dmat, &priv->nullseg, 1); |
214 | goto fail0; |
215 | } |
216 | |
217 | /* XXX errno NetBSD->Linux */ |
218 | ret = -bus_dmamem_map(dmat, &priv->nullseg, 1, PAGE_SIZE, |
219 | &priv->nullp, BUS_DMA_WAITOK); |
220 | if (ret) { |
221 | fail2: bus_dmamap_destroy(dmat, priv->nullmap); |
222 | goto fail1; |
223 | } |
224 | |
225 | /* XXX errno NetBSD->Linux */ |
226 | ret = -bus_dmamap_load(dmat, priv->nullmap, priv->nullp, PAGE_SIZE, |
227 | NULL, BUS_DMA_WAITOK); |
228 | if (ret) { |
229 | fail3: __unused bus_dmamem_unmap(dmat, priv->nullp, PAGE_SIZE); |
230 | goto fail2; |
231 | } |
232 | priv->null = priv->nullmap->dm_segs[0].ds_addr; |
233 | } |
234 | #else |
235 | priv->nullp = pci_alloc_consistent(device->pdev, 16 * 1024, &priv->null); |
236 | if (!priv->nullp) { |
237 | nv_error(priv, "unable to allocate dummy pages\n" ); |
238 | return -ENOMEM; |
239 | } |
240 | #endif |
241 | |
242 | ret = nouveau_vm_create(&priv->base, 0, NV44_GART_SIZE, 0, 4096, |
243 | &priv->vm); |
244 | if (ret) |
245 | return ret; |
246 | |
247 | ret = nouveau_gpuobj_new(nv_object(priv), NULL, |
248 | (NV44_GART_SIZE / NV44_GART_PAGE) * 4, |
249 | 512 * 1024, NVOBJ_FLAG_ZERO_ALLOC, |
250 | &priv->vm->pgt[0].obj[0]); |
251 | priv->vm->pgt[0].refcount[0] = 1; |
252 | if (ret) |
253 | return ret; |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static int |
259 | nv44_vmmgr_init(struct nouveau_object *object) |
260 | { |
261 | struct nv04_vmmgr_priv *priv = (void *)object; |
262 | struct nouveau_gpuobj *gart = priv->vm->pgt[0].obj[0]; |
263 | u32 addr; |
264 | int ret; |
265 | |
266 | ret = nouveau_vmmgr_init(&priv->base); |
267 | if (ret) |
268 | return ret; |
269 | |
270 | /* calculate vram address of this PRAMIN block, object must be |
271 | * allocated on 512KiB alignment, and not exceed a total size |
272 | * of 512KiB for this to work correctly |
273 | */ |
274 | addr = nv_rd32(priv, 0x10020c); |
275 | addr -= ((gart->addr >> 19) + 1) << 19; |
276 | |
277 | nv_wr32(priv, 0x100850, 0x80000000); |
278 | nv_wr32(priv, 0x100818, priv->null); |
279 | nv_wr32(priv, 0x100804, NV44_GART_SIZE); |
280 | nv_wr32(priv, 0x100850, 0x00008000); |
281 | nv_mask(priv, 0x10008c, 0x00000200, 0x00000200); |
282 | nv_wr32(priv, 0x100820, 0x00000000); |
283 | nv_wr32(priv, 0x10082c, 0x00000001); |
284 | nv_wr32(priv, 0x100800, addr | 0x00000010); |
285 | return 0; |
286 | } |
287 | |
288 | struct nouveau_oclass |
289 | nv44_vmmgr_oclass = { |
290 | .handle = NV_SUBDEV(VM, 0x44), |
291 | .ofuncs = &(struct nouveau_ofuncs) { |
292 | .ctor = nv44_vmmgr_ctor, |
293 | .dtor = nv04_vmmgr_dtor, |
294 | .init = nv44_vmmgr_init, |
295 | .fini = _nouveau_vmmgr_fini, |
296 | }, |
297 | }; |
298 | |