Commit | Line | Data |
---|---|---|
b7bc613a BS |
1 | /* |
2 | * Copyright 2010 Red Hat Inc. | |
3 | * | |
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: | |
10 | * | |
11 | * The above copyright notice and this permission notice shall be included in | |
12 | * all copies or substantial portions of the Software. | |
13 | * | |
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 COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
20 | * OTHER DEALINGS IN THE SOFTWARE. | |
21 | * | |
22 | * Authors: Ben Skeggs | |
23 | */ | |
24 | ||
25 | #include "drmP.h" | |
26 | ||
27 | #include "nouveau_drv.h" | |
28 | #include "nouveau_dma.h" | |
ef8389a8 | 29 | #include "nv50_display.h" |
b7bc613a | 30 | |
ebb945a9 BS |
31 | static u32 |
32 | nv50_evo_rd32(struct nouveau_object *object, u32 addr) | |
33 | { | |
34 | void __iomem *iomem = object->oclass->ofuncs->rd08; | |
35 | return ioread32_native(iomem + addr); | |
36 | } | |
37 | ||
38 | static void | |
39 | nv50_evo_wr32(struct nouveau_object *object, u32 addr, u32 data) | |
40 | { | |
41 | void __iomem *iomem = object->oclass->ofuncs->rd08; | |
42 | iowrite32_native(data, iomem + addr); | |
43 | } | |
44 | ||
b7bc613a | 45 | static void |
1e96268a | 46 | nv50_evo_channel_del(struct nouveau_channel **pevo) |
b7bc613a | 47 | { |
1e96268a | 48 | struct nouveau_channel *evo = *pevo; |
b7bc613a | 49 | |
1e96268a | 50 | if (!evo) |
b7bc613a | 51 | return; |
1e96268a | 52 | *pevo = NULL; |
b7bc613a | 53 | |
ebb945a9 BS |
54 | nouveau_bo_unmap(evo->push.buffer); |
55 | nouveau_bo_ref(NULL, &evo->push.buffer); | |
b7bc613a | 56 | |
ebb945a9 BS |
57 | if (evo->object) |
58 | iounmap(evo->object->oclass->ofuncs); | |
1e96268a BS |
59 | |
60 | kfree(evo); | |
b7bc613a BS |
61 | } |
62 | ||
ebb945a9 BS |
63 | int |
64 | nv50_evo_dmaobj_new(struct nouveau_channel *evo, u32 handle, u32 memtype, | |
65 | u64 base, u64 size, struct nouveau_gpuobj **pobj) | |
292deb7a | 66 | { |
ebb945a9 BS |
67 | struct drm_device *dev = evo->fence; |
68 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
69 | struct nv50_display *disp = nv50_display(dev); | |
70 | u32 dmao = disp->dmao; | |
71 | u32 hash = disp->hash; | |
292deb7a BS |
72 | u32 flags5; |
73 | ||
74 | if (dev_priv->chipset < 0xc0) { | |
75 | /* not supported on 0x50, specified in format mthd */ | |
76 | if (dev_priv->chipset == 0x50) | |
77 | memtype = 0; | |
78 | flags5 = 0x00010000; | |
79 | } else { | |
80 | if (memtype & 0x80000000) | |
81 | flags5 = 0x00000000; /* large pages */ | |
82 | else | |
83 | flags5 = 0x00020000; | |
84 | } | |
85 | ||
ebb945a9 BS |
86 | nv_wo32(disp->ramin, dmao + 0x00, 0x0019003d | (memtype << 22)); |
87 | nv_wo32(disp->ramin, dmao + 0x04, lower_32_bits(base + size - 1)); | |
88 | nv_wo32(disp->ramin, dmao + 0x08, lower_32_bits(base)); | |
89 | nv_wo32(disp->ramin, dmao + 0x0c, upper_32_bits(base + size - 1) << 24 | | |
90 | upper_32_bits(base)); | |
91 | nv_wo32(disp->ramin, dmao + 0x10, 0x00000000); | |
92 | nv_wo32(disp->ramin, dmao + 0x14, flags5); | |
292deb7a | 93 | |
ebb945a9 BS |
94 | nv_wo32(disp->ramin, hash + 0x00, handle); |
95 | nv_wo32(disp->ramin, hash + 0x04, (evo->handle << 28) | (dmao << 10) | | |
96 | evo->handle); | |
b7bc613a | 97 | |
ebb945a9 BS |
98 | disp->dmao += 0x20; |
99 | disp->hash += 0x08; | |
100 | return 0; | |
b7bc613a BS |
101 | } |
102 | ||
103 | static int | |
30d81817 BS |
104 | nv50_evo_channel_new(struct drm_device *dev, int chid, |
105 | struct nouveau_channel **pevo) | |
b7bc613a | 106 | { |
ef8389a8 | 107 | struct nv50_display *disp = nv50_display(dev); |
1e96268a | 108 | struct nouveau_channel *evo; |
b7bc613a BS |
109 | int ret; |
110 | ||
1e96268a BS |
111 | evo = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL); |
112 | if (!evo) | |
b7bc613a | 113 | return -ENOMEM; |
1e96268a | 114 | *pevo = evo; |
b7bc613a | 115 | |
ebb945a9 BS |
116 | evo->handle = chid; |
117 | evo->fence = dev; | |
1e96268a BS |
118 | evo->user_get = 4; |
119 | evo->user_put = 0; | |
b7bc613a | 120 | |
22b33e8e | 121 | ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, NULL, |
ebb945a9 | 122 | &evo->push.buffer); |
b7bc613a | 123 | if (ret == 0) |
ebb945a9 | 124 | ret = nouveau_bo_pin(evo->push.buffer, TTM_PL_FLAG_VRAM); |
b7bc613a BS |
125 | if (ret) { |
126 | NV_ERROR(dev, "Error creating EVO DMA push buffer: %d\n", ret); | |
1e96268a | 127 | nv50_evo_channel_del(pevo); |
b7bc613a BS |
128 | return ret; |
129 | } | |
130 | ||
ebb945a9 | 131 | ret = nouveau_bo_map(evo->push.buffer); |
b7bc613a BS |
132 | if (ret) { |
133 | NV_ERROR(dev, "Error mapping EVO DMA push buffer: %d\n", ret); | |
1e96268a | 134 | nv50_evo_channel_del(pevo); |
b7bc613a BS |
135 | return ret; |
136 | } | |
137 | ||
ebb945a9 BS |
138 | evo->object = kzalloc(sizeof(*evo->object), GFP_KERNEL); |
139 | #ifdef NOUVEAU_OBJECT_MAGIC | |
140 | evo->object->_magic = NOUVEAU_OBJECT_MAGIC; | |
141 | #endif | |
142 | evo->object->parent = nv_object(disp->ramin)->parent; | |
143 | evo->object->engine = nv_object(disp->ramin)->engine; | |
144 | evo->object->oclass = | |
145 | kzalloc(sizeof(*evo->object->oclass), GFP_KERNEL); | |
146 | evo->object->oclass->ofuncs = | |
147 | kzalloc(sizeof(*evo->object->oclass->ofuncs), GFP_KERNEL); | |
148 | evo->object->oclass->ofuncs->rd32 = nv50_evo_rd32; | |
149 | evo->object->oclass->ofuncs->wr32 = nv50_evo_wr32; | |
150 | evo->object->oclass->ofuncs->rd08 = | |
151 | ioremap(pci_resource_start(dev->pdev, 0) + | |
152 | NV50_PDISPLAY_USER(evo->handle), PAGE_SIZE); | |
b7bc613a BS |
153 | return 0; |
154 | } | |
155 | ||
156 | static int | |
157 | nv50_evo_channel_init(struct nouveau_channel *evo) | |
158 | { | |
ebb945a9 BS |
159 | struct drm_device *dev = evo->fence; |
160 | int id = evo->handle, ret, i; | |
161 | u64 pushbuf = evo->push.buffer->bo.offset; | |
b7bc613a BS |
162 | u32 tmp; |
163 | ||
43ce028f BS |
164 | tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id)); |
165 | if ((tmp & 0x009f0000) == 0x00020000) | |
166 | nv_wr32(dev, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00800000); | |
b7bc613a | 167 | |
43ce028f BS |
168 | tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id)); |
169 | if ((tmp & 0x003f0000) == 0x00030000) | |
170 | nv_wr32(dev, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00600000); | |
b7bc613a BS |
171 | |
172 | /* initialise fifo */ | |
43ce028f BS |
173 | nv_wr32(dev, NV50_PDISPLAY_EVO_DMA_CB(id), pushbuf >> 8 | |
174 | NV50_PDISPLAY_EVO_DMA_CB_LOCATION_VRAM | | |
175 | NV50_PDISPLAY_EVO_DMA_CB_VALID); | |
1e96268a BS |
176 | nv_wr32(dev, NV50_PDISPLAY_EVO_UNK2(id), 0x00010000); |
177 | nv_wr32(dev, NV50_PDISPLAY_EVO_HASH_TAG(id), id); | |
43ce028f BS |
178 | nv_mask(dev, NV50_PDISPLAY_EVO_CTRL(id), NV50_PDISPLAY_EVO_CTRL_DMA, |
179 | NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED); | |
180 | ||
181 | nv_wr32(dev, NV50_PDISPLAY_USER_PUT(id), 0x00000000); | |
182 | nv_wr32(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x01000003 | | |
183 | NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED); | |
1e96268a | 184 | if (!nv_wait(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x80000000, 0x00000000)) { |
43ce028f | 185 | NV_ERROR(dev, "EvoCh %d init timeout: 0x%08x\n", id, |
1e96268a | 186 | nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id))); |
b7bc613a BS |
187 | return -EBUSY; |
188 | } | |
b7bc613a BS |
189 | |
190 | /* enable error reporting on the channel */ | |
1e96268a | 191 | nv_mask(dev, 0x610028, 0x00000000, 0x00010001 << id); |
b7bc613a BS |
192 | |
193 | evo->dma.max = (4096/4) - 2; | |
59197c02 | 194 | evo->dma.max &= ~7; |
b7bc613a BS |
195 | evo->dma.put = 0; |
196 | evo->dma.cur = evo->dma.put; | |
197 | evo->dma.free = evo->dma.max - evo->dma.cur; | |
198 | ||
199 | ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS); | |
200 | if (ret) | |
201 | return ret; | |
202 | ||
203 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | |
204 | OUT_RING(evo, 0); | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
209 | static void | |
210 | nv50_evo_channel_fini(struct nouveau_channel *evo) | |
211 | { | |
ebb945a9 BS |
212 | struct drm_device *dev = evo->fence; |
213 | int id = evo->handle; | |
43ce028f BS |
214 | |
215 | nv_mask(dev, 0x610028, 0x00010001 << id, 0x00000000); | |
216 | nv_mask(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x00001010, 0x00001000); | |
217 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, (1 << id)); | |
218 | nv_mask(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x00000003, 0x00000000); | |
219 | if (!nv_wait(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x001e0000, 0x00000000)) { | |
220 | NV_ERROR(dev, "EvoCh %d takedown timeout: 0x%08x\n", id, | |
221 | nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id))); | |
1e96268a BS |
222 | } |
223 | } | |
224 | ||
1772fcc6 | 225 | void |
33f409df BS |
226 | nv50_evo_destroy(struct drm_device *dev) |
227 | { | |
228 | struct nv50_display *disp = nv50_display(dev); | |
cdccc70e BS |
229 | int i; |
230 | ||
231 | for (i = 0; i < 2; i++) { | |
232 | if (disp->crtc[i].sem.bo) { | |
233 | nouveau_bo_unmap(disp->crtc[i].sem.bo); | |
234 | nouveau_bo_ref(NULL, &disp->crtc[i].sem.bo); | |
235 | } | |
236 | nv50_evo_channel_del(&disp->crtc[i].sync); | |
237 | } | |
33f409df | 238 | nv50_evo_channel_del(&disp->master); |
ebb945a9 | 239 | nouveau_gpuobj_ref(NULL, &disp->ramin); |
33f409df BS |
240 | } |
241 | ||
1772fcc6 | 242 | int |
1e96268a BS |
243 | nv50_evo_create(struct drm_device *dev) |
244 | { | |
245 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
ef8389a8 | 246 | struct nv50_display *disp = nv50_display(dev); |
1e96268a | 247 | struct nouveau_channel *evo; |
cdccc70e | 248 | int ret, i, j; |
1e96268a | 249 | |
1e96268a BS |
250 | /* setup object management on it, any other evo channel will |
251 | * use this also as there's no per-channel support on the | |
252 | * hardware | |
253 | */ | |
8888cb18 | 254 | ret = nouveau_gpuobj_new(dev, NULL, 32768, 65536, |
ebb945a9 | 255 | NVOBJ_FLAG_ZERO_ALLOC, &disp->ramin); |
1e96268a BS |
256 | if (ret) { |
257 | NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret); | |
33f409df | 258 | goto err; |
1e96268a BS |
259 | } |
260 | ||
ebb945a9 BS |
261 | disp->hash = 0x0000; |
262 | disp->dmao = 0x1000; | |
1e96268a | 263 | |
ebb945a9 BS |
264 | /* create primary evo channel, the one we use for modesetting |
265 | * purporses | |
60f60bf1 | 266 | */ |
ebb945a9 | 267 | ret = nv50_evo_channel_new(dev, 0, &disp->master); |
60f60bf1 | 268 | if (ret) |
ebb945a9 BS |
269 | return ret; |
270 | evo = disp->master; | |
60f60bf1 | 271 | |
292deb7a | 272 | ret = nv50_evo_dmaobj_new(disp->master, NvEvoSync, 0x0000, |
ebb945a9 | 273 | disp->ramin->addr + 0x2000, 0x1000, NULL); |
60f60bf1 BS |
274 | if (ret) |
275 | goto err; | |
276 | ||
1e96268a | 277 | /* create some default objects for the scanout memtypes we support */ |
292deb7a | 278 | ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM, 0x0000, |
861d2107 | 279 | 0, nvfb_vram_size(dev), NULL); |
292deb7a BS |
280 | if (ret) |
281 | goto err; | |
1e96268a | 282 | |
292deb7a | 283 | ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM_LP, 0x80000000, |
861d2107 | 284 | 0, nvfb_vram_size(dev), NULL); |
292deb7a BS |
285 | if (ret) |
286 | goto err; | |
1e96268a | 287 | |
292deb7a | 288 | ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB32, 0x80000000 | |
ebb945a9 | 289 | (dev_priv->chipset < 0xc0 ? 0x7a : 0xfe), |
861d2107 | 290 | 0, nvfb_vram_size(dev), NULL); |
292deb7a BS |
291 | if (ret) |
292 | goto err; | |
6d86951a | 293 | |
292deb7a | 294 | ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB16, 0x80000000 | |
ebb945a9 | 295 | (dev_priv->chipset < 0xc0 ? 0x70 : 0xfe), |
861d2107 | 296 | 0, nvfb_vram_size(dev), NULL); |
292deb7a BS |
297 | if (ret) |
298 | goto err; | |
1e96268a | 299 | |
cdccc70e BS |
300 | /* create "display sync" channels and other structures we need |
301 | * to implement page flipping | |
302 | */ | |
303 | for (i = 0; i < 2; i++) { | |
304 | struct nv50_display_crtc *dispc = &disp->crtc[i]; | |
305 | u64 offset; | |
306 | ||
307 | ret = nv50_evo_channel_new(dev, 1 + i, &dispc->sync); | |
308 | if (ret) | |
309 | goto err; | |
310 | ||
7375c95b | 311 | ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, |
22b33e8e | 312 | 0, 0x0000, NULL, &dispc->sem.bo); |
cdccc70e | 313 | if (!ret) { |
cdccc70e BS |
314 | ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM); |
315 | if (!ret) | |
316 | ret = nouveau_bo_map(dispc->sem.bo); | |
317 | if (ret) | |
318 | nouveau_bo_ref(NULL, &dispc->sem.bo); | |
180cc306 | 319 | offset = dispc->sem.bo->bo.offset; |
cdccc70e BS |
320 | } |
321 | ||
322 | if (ret) | |
323 | goto err; | |
324 | ||
325 | ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoSync, 0x0000, | |
326 | offset, 4096, NULL); | |
327 | if (ret) | |
328 | goto err; | |
329 | ||
330 | ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoVRAM_LP, 0x80000000, | |
861d2107 | 331 | 0, nvfb_vram_size(dev), NULL); |
cdccc70e BS |
332 | if (ret) |
333 | goto err; | |
334 | ||
335 | ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB32, 0x80000000 | | |
336 | (dev_priv->chipset < 0xc0 ? | |
ebb945a9 | 337 | 0x7a : 0xfe), |
861d2107 | 338 | 0, nvfb_vram_size(dev), NULL); |
cdccc70e BS |
339 | if (ret) |
340 | goto err; | |
341 | ||
342 | ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB16, 0x80000000 | | |
343 | (dev_priv->chipset < 0xc0 ? | |
ebb945a9 | 344 | 0x70 : 0xfe), |
861d2107 | 345 | 0, nvfb_vram_size(dev), NULL); |
cdccc70e BS |
346 | if (ret) |
347 | goto err; | |
348 | ||
349 | for (j = 0; j < 4096; j += 4) | |
350 | nouveau_bo_wr32(dispc->sem.bo, j / 4, 0x74b1e000); | |
351 | dispc->sem.offset = 0; | |
352 | } | |
353 | ||
1e96268a | 354 | return 0; |
33f409df BS |
355 | |
356 | err: | |
357 | nv50_evo_destroy(dev); | |
358 | return ret; | |
b7bc613a BS |
359 | } |
360 | ||
361 | int | |
362 | nv50_evo_init(struct drm_device *dev) | |
363 | { | |
ef8389a8 | 364 | struct nv50_display *disp = nv50_display(dev); |
cdccc70e | 365 | int ret, i; |
b7bc613a | 366 | |
cdccc70e BS |
367 | ret = nv50_evo_channel_init(disp->master); |
368 | if (ret) | |
369 | return ret; | |
370 | ||
371 | for (i = 0; i < 2; i++) { | |
372 | ret = nv50_evo_channel_init(disp->crtc[i].sync); | |
373 | if (ret) | |
374 | return ret; | |
375 | } | |
376 | ||
377 | return 0; | |
b7bc613a BS |
378 | } |
379 | ||
380 | void | |
381 | nv50_evo_fini(struct drm_device *dev) | |
382 | { | |
ef8389a8 | 383 | struct nv50_display *disp = nv50_display(dev); |
cdccc70e BS |
384 | int i; |
385 | ||
386 | for (i = 0; i < 2; i++) { | |
387 | if (disp->crtc[i].sync) | |
388 | nv50_evo_channel_fini(disp->crtc[i].sync); | |
389 | } | |
b7bc613a | 390 | |
33f409df | 391 | if (disp->master) |
59c0f578 | 392 | nv50_evo_channel_fini(disp->master); |
b7bc613a | 393 | } |