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" | |
29 | #include "nouveau_ramht.h" | |
30 | ||
31 | static void | |
1e96268a | 32 | nv50_evo_channel_del(struct nouveau_channel **pevo) |
b7bc613a | 33 | { |
1e96268a BS |
34 | struct drm_nouveau_private *dev_priv; |
35 | struct nouveau_channel *evo = *pevo; | |
b7bc613a | 36 | |
1e96268a | 37 | if (!evo) |
b7bc613a | 38 | return; |
1e96268a | 39 | *pevo = NULL; |
b7bc613a | 40 | |
1e96268a BS |
41 | dev_priv = evo->dev->dev_private; |
42 | dev_priv->evo_alloc &= ~(1 << evo->id); | |
b7bc613a | 43 | |
1e96268a BS |
44 | nouveau_gpuobj_channel_takedown(evo); |
45 | nouveau_bo_unmap(evo->pushbuf_bo); | |
46 | nouveau_bo_ref(NULL, &evo->pushbuf_bo); | |
b7bc613a | 47 | |
1e96268a BS |
48 | if (evo->user) |
49 | iounmap(evo->user); | |
50 | ||
51 | kfree(evo); | |
b7bc613a BS |
52 | } |
53 | ||
54 | int | |
55 | nv50_evo_dmaobj_new(struct nouveau_channel *evo, u32 class, u32 name, | |
6d86951a BS |
56 | u32 tile_flags, u32 magic_flags, u32 offset, u32 limit, |
57 | u32 flags5) | |
b7bc613a BS |
58 | { |
59 | struct drm_nouveau_private *dev_priv = evo->dev->dev_private; | |
60 | struct drm_device *dev = evo->dev; | |
61 | struct nouveau_gpuobj *obj = NULL; | |
62 | int ret; | |
63 | ||
1e96268a | 64 | ret = nouveau_gpuobj_new(dev, dev_priv->evo, 6*4, 32, 0, &obj); |
b7bc613a BS |
65 | if (ret) |
66 | return ret; | |
67 | obj->engine = NVOBJ_ENGINE_DISPLAY; | |
68 | ||
69 | nv_wo32(obj, 0, (tile_flags << 22) | (magic_flags << 16) | class); | |
70 | nv_wo32(obj, 4, limit); | |
71 | nv_wo32(obj, 8, offset); | |
72 | nv_wo32(obj, 12, 0x00000000); | |
73 | nv_wo32(obj, 16, 0x00000000); | |
6d86951a | 74 | nv_wo32(obj, 20, flags5); |
b7bc613a BS |
75 | dev_priv->engine.instmem.flush(dev); |
76 | ||
77 | ret = nouveau_ramht_insert(evo, name, obj); | |
78 | nouveau_gpuobj_ref(NULL, &obj); | |
79 | if (ret) { | |
80 | return ret; | |
81 | } | |
82 | ||
83 | return 0; | |
84 | } | |
85 | ||
86 | static int | |
1e96268a | 87 | nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pevo) |
b7bc613a BS |
88 | { |
89 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
1e96268a | 90 | struct nouveau_channel *evo; |
b7bc613a BS |
91 | int ret; |
92 | ||
1e96268a BS |
93 | evo = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL); |
94 | if (!evo) | |
b7bc613a | 95 | return -ENOMEM; |
1e96268a | 96 | *pevo = evo; |
b7bc613a | 97 | |
1e96268a BS |
98 | for (evo->id = 0; evo->id < 5; evo->id++) { |
99 | if (dev_priv->evo_alloc & (1 << evo->id)) | |
100 | continue; | |
b7bc613a | 101 | |
1e96268a BS |
102 | dev_priv->evo_alloc |= (1 << evo->id); |
103 | break; | |
b7bc613a BS |
104 | } |
105 | ||
1e96268a BS |
106 | if (evo->id == 5) { |
107 | kfree(evo); | |
108 | return -ENODEV; | |
b7bc613a BS |
109 | } |
110 | ||
1e96268a BS |
111 | evo->dev = dev; |
112 | evo->user_get = 4; | |
113 | evo->user_put = 0; | |
b7bc613a BS |
114 | |
115 | ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, | |
1e96268a | 116 | false, true, &evo->pushbuf_bo); |
b7bc613a | 117 | if (ret == 0) |
1e96268a | 118 | ret = nouveau_bo_pin(evo->pushbuf_bo, TTM_PL_FLAG_VRAM); |
b7bc613a BS |
119 | if (ret) { |
120 | NV_ERROR(dev, "Error creating EVO DMA push buffer: %d\n", ret); | |
1e96268a | 121 | nv50_evo_channel_del(pevo); |
b7bc613a BS |
122 | return ret; |
123 | } | |
124 | ||
1e96268a | 125 | ret = nouveau_bo_map(evo->pushbuf_bo); |
b7bc613a BS |
126 | if (ret) { |
127 | NV_ERROR(dev, "Error mapping EVO DMA push buffer: %d\n", ret); | |
1e96268a | 128 | nv50_evo_channel_del(pevo); |
b7bc613a BS |
129 | return ret; |
130 | } | |
131 | ||
1e96268a BS |
132 | evo->user = ioremap(pci_resource_start(dev->pdev, 0) + |
133 | NV50_PDISPLAY_USER(evo->id), PAGE_SIZE); | |
134 | if (!evo->user) { | |
b7bc613a | 135 | NV_ERROR(dev, "Error mapping EVO control regs.\n"); |
1e96268a | 136 | nv50_evo_channel_del(pevo); |
b7bc613a BS |
137 | return -ENOMEM; |
138 | } | |
139 | ||
1e96268a BS |
140 | /* bind primary evo channel's ramht to the channel */ |
141 | if (dev_priv->evo && evo != dev_priv->evo) | |
142 | nouveau_ramht_ref(dev_priv->evo->ramht, &evo->ramht, NULL); | |
143 | ||
b7bc613a BS |
144 | return 0; |
145 | } | |
146 | ||
147 | static int | |
148 | nv50_evo_channel_init(struct nouveau_channel *evo) | |
149 | { | |
b7bc613a | 150 | struct drm_device *dev = evo->dev; |
1e96268a | 151 | int id = evo->id, ret, i; |
43ce028f | 152 | u64 pushbuf = evo->pushbuf_bo->bo.mem.start << PAGE_SHIFT; |
b7bc613a BS |
153 | u32 tmp; |
154 | ||
43ce028f BS |
155 | tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id)); |
156 | if ((tmp & 0x009f0000) == 0x00020000) | |
157 | nv_wr32(dev, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00800000); | |
b7bc613a | 158 | |
43ce028f BS |
159 | tmp = nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id)); |
160 | if ((tmp & 0x003f0000) == 0x00030000) | |
161 | nv_wr32(dev, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00600000); | |
b7bc613a BS |
162 | |
163 | /* initialise fifo */ | |
43ce028f BS |
164 | nv_wr32(dev, NV50_PDISPLAY_EVO_DMA_CB(id), pushbuf >> 8 | |
165 | NV50_PDISPLAY_EVO_DMA_CB_LOCATION_VRAM | | |
166 | NV50_PDISPLAY_EVO_DMA_CB_VALID); | |
1e96268a BS |
167 | nv_wr32(dev, NV50_PDISPLAY_EVO_UNK2(id), 0x00010000); |
168 | nv_wr32(dev, NV50_PDISPLAY_EVO_HASH_TAG(id), id); | |
43ce028f BS |
169 | nv_mask(dev, NV50_PDISPLAY_EVO_CTRL(id), NV50_PDISPLAY_EVO_CTRL_DMA, |
170 | NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED); | |
171 | ||
172 | nv_wr32(dev, NV50_PDISPLAY_USER_PUT(id), 0x00000000); | |
173 | nv_wr32(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x01000003 | | |
174 | NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED); | |
1e96268a | 175 | if (!nv_wait(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x80000000, 0x00000000)) { |
43ce028f | 176 | NV_ERROR(dev, "EvoCh %d init timeout: 0x%08x\n", id, |
1e96268a | 177 | nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id))); |
b7bc613a BS |
178 | return -EBUSY; |
179 | } | |
b7bc613a BS |
180 | |
181 | /* enable error reporting on the channel */ | |
1e96268a | 182 | nv_mask(dev, 0x610028, 0x00000000, 0x00010001 << id); |
b7bc613a BS |
183 | |
184 | evo->dma.max = (4096/4) - 2; | |
185 | evo->dma.put = 0; | |
186 | evo->dma.cur = evo->dma.put; | |
187 | evo->dma.free = evo->dma.max - evo->dma.cur; | |
188 | ||
189 | ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS); | |
190 | if (ret) | |
191 | return ret; | |
192 | ||
193 | for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) | |
194 | OUT_RING(evo, 0); | |
195 | ||
196 | return 0; | |
197 | } | |
198 | ||
199 | static void | |
200 | nv50_evo_channel_fini(struct nouveau_channel *evo) | |
201 | { | |
202 | struct drm_device *dev = evo->dev; | |
43ce028f BS |
203 | int id = evo->id; |
204 | ||
205 | nv_mask(dev, 0x610028, 0x00010001 << id, 0x00000000); | |
206 | nv_mask(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x00001010, 0x00001000); | |
207 | nv_wr32(dev, NV50_PDISPLAY_INTR_0, (1 << id)); | |
208 | nv_mask(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x00000003, 0x00000000); | |
209 | if (!nv_wait(dev, NV50_PDISPLAY_EVO_CTRL(id), 0x001e0000, 0x00000000)) { | |
210 | NV_ERROR(dev, "EvoCh %d takedown timeout: 0x%08x\n", id, | |
211 | nv_rd32(dev, NV50_PDISPLAY_EVO_CTRL(id))); | |
1e96268a BS |
212 | } |
213 | } | |
214 | ||
215 | static int | |
216 | nv50_evo_create(struct drm_device *dev) | |
217 | { | |
218 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
219 | struct nouveau_gpuobj *ramht = NULL; | |
220 | struct nouveau_channel *evo; | |
221 | int ret; | |
222 | ||
223 | /* create primary evo channel, the one we use for modesetting | |
224 | * purporses | |
225 | */ | |
226 | ret = nv50_evo_channel_new(dev, &dev_priv->evo); | |
227 | if (ret) | |
228 | return ret; | |
229 | evo = dev_priv->evo; | |
230 | ||
231 | /* setup object management on it, any other evo channel will | |
232 | * use this also as there's no per-channel support on the | |
233 | * hardware | |
234 | */ | |
8888cb18 | 235 | ret = nouveau_gpuobj_new(dev, NULL, 32768, 65536, |
1e96268a BS |
236 | NVOBJ_FLAG_ZERO_ALLOC, &evo->ramin); |
237 | if (ret) { | |
238 | NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret); | |
239 | nv50_evo_channel_del(&dev_priv->evo); | |
240 | return ret; | |
241 | } | |
242 | ||
243 | ret = drm_mm_init(&evo->ramin_heap, 0, 32768); | |
244 | if (ret) { | |
245 | NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret); | |
246 | nv50_evo_channel_del(&dev_priv->evo); | |
247 | return ret; | |
248 | } | |
249 | ||
250 | ret = nouveau_gpuobj_new(dev, evo, 4096, 16, 0, &ramht); | |
251 | if (ret) { | |
252 | NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret); | |
253 | nv50_evo_channel_del(&dev_priv->evo); | |
254 | return ret; | |
255 | } | |
256 | ||
257 | ret = nouveau_ramht_new(dev, ramht, &evo->ramht); | |
258 | nouveau_gpuobj_ref(NULL, &ramht); | |
259 | if (ret) { | |
260 | nv50_evo_channel_del(&dev_priv->evo); | |
261 | return ret; | |
262 | } | |
263 | ||
264 | /* create some default objects for the scanout memtypes we support */ | |
6d86951a BS |
265 | if (dev_priv->card_type >= NV_C0) { |
266 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0xfe, 0x19, | |
267 | 0, 0xffffffff, 0x00000000); | |
268 | if (ret) { | |
269 | nv50_evo_channel_del(&dev_priv->evo); | |
270 | return ret; | |
271 | } | |
272 | ||
273 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM, 0, 0x19, | |
274 | 0, dev_priv->vram_size, 0x00020000); | |
275 | if (ret) { | |
276 | nv50_evo_channel_del(&dev_priv->evo); | |
277 | return ret; | |
278 | } | |
279 | ||
280 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM_LP, 0, 0x19, | |
281 | 0, dev_priv->vram_size, 0x00000000); | |
282 | if (ret) { | |
283 | nv50_evo_channel_del(&dev_priv->evo); | |
284 | return ret; | |
285 | } | |
c4534fdf | 286 | } else { |
1e96268a | 287 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB16, 0x70, 0x19, |
6d86951a | 288 | 0, 0xffffffff, 0x00010000); |
1e96268a BS |
289 | if (ret) { |
290 | nv50_evo_channel_del(&dev_priv->evo); | |
291 | return ret; | |
292 | } | |
293 | ||
294 | ||
295 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoFB32, 0x7a, 0x19, | |
6d86951a | 296 | 0, 0xffffffff, 0x00010000); |
1e96268a BS |
297 | if (ret) { |
298 | nv50_evo_channel_del(&dev_priv->evo); | |
299 | return ret; | |
300 | } | |
1e96268a | 301 | |
6d86951a BS |
302 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM, 0, 0x19, |
303 | 0, dev_priv->vram_size, 0x00010000); | |
304 | if (ret) { | |
305 | nv50_evo_channel_del(&dev_priv->evo); | |
306 | return ret; | |
307 | } | |
308 | ||
309 | ret = nv50_evo_dmaobj_new(evo, 0x3d, NvEvoVRAM_LP, 0, 0x19, | |
310 | 0, dev_priv->vram_size, 0x00010000); | |
311 | if (ret) { | |
312 | nv50_evo_channel_del(&dev_priv->evo); | |
313 | return ret; | |
314 | } | |
b7bc613a | 315 | } |
1e96268a BS |
316 | |
317 | return 0; | |
b7bc613a BS |
318 | } |
319 | ||
320 | int | |
321 | nv50_evo_init(struct drm_device *dev) | |
322 | { | |
323 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
324 | int ret; | |
325 | ||
326 | if (!dev_priv->evo) { | |
1e96268a | 327 | ret = nv50_evo_create(dev); |
b7bc613a BS |
328 | if (ret) |
329 | return ret; | |
330 | } | |
331 | ||
332 | return nv50_evo_channel_init(dev_priv->evo); | |
333 | } | |
334 | ||
335 | void | |
336 | nv50_evo_fini(struct drm_device *dev) | |
337 | { | |
338 | struct drm_nouveau_private *dev_priv = dev->dev_private; | |
339 | ||
340 | if (dev_priv->evo) { | |
341 | nv50_evo_channel_fini(dev_priv->evo); | |
342 | nv50_evo_channel_del(&dev_priv->evo); | |
343 | } | |
344 | } |