diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_mem.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_mem.c | 215 |
1 files changed, 171 insertions, 44 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 5158a12f7844..2dc09dbd817d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c | |||
@@ -192,6 +192,92 @@ void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap) | |||
192 | } | 192 | } |
193 | 193 | ||
194 | /* | 194 | /* |
195 | * NV10-NV40 tiling helpers | ||
196 | */ | ||
197 | |||
198 | static void | ||
199 | nv10_mem_set_region_tiling(struct drm_device *dev, int i, uint32_t addr, | ||
200 | uint32_t size, uint32_t pitch) | ||
201 | { | ||
202 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
203 | struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo; | ||
204 | struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; | ||
205 | struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph; | ||
206 | struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; | ||
207 | |||
208 | tile->addr = addr; | ||
209 | tile->size = size; | ||
210 | tile->used = !!pitch; | ||
211 | nouveau_fence_unref((void **)&tile->fence); | ||
212 | |||
213 | if (!pfifo->cache_flush(dev)) | ||
214 | return; | ||
215 | |||
216 | pfifo->reassign(dev, false); | ||
217 | pfifo->cache_flush(dev); | ||
218 | pfifo->cache_pull(dev, false); | ||
219 | |||
220 | nouveau_wait_for_idle(dev); | ||
221 | |||
222 | pgraph->set_region_tiling(dev, i, addr, size, pitch); | ||
223 | pfb->set_region_tiling(dev, i, addr, size, pitch); | ||
224 | |||
225 | pfifo->cache_pull(dev, true); | ||
226 | pfifo->reassign(dev, true); | ||
227 | } | ||
228 | |||
229 | struct nouveau_tile_reg * | ||
230 | nv10_mem_set_tiling(struct drm_device *dev, uint32_t addr, uint32_t size, | ||
231 | uint32_t pitch) | ||
232 | { | ||
233 | struct drm_nouveau_private *dev_priv = dev->dev_private; | ||
234 | struct nouveau_fb_engine *pfb = &dev_priv->engine.fb; | ||
235 | struct nouveau_tile_reg *tile = dev_priv->tile.reg, *found = NULL; | ||
236 | int i; | ||
237 | |||
238 | spin_lock(&dev_priv->tile.lock); | ||
239 | |||
240 | for (i = 0; i < pfb->num_tiles; i++) { | ||
241 | if (tile[i].used) | ||
242 | /* Tile region in use. */ | ||
243 | continue; | ||
244 | |||
245 | if (tile[i].fence && | ||
246 | !nouveau_fence_signalled(tile[i].fence, NULL)) | ||
247 | /* Pending tile region. */ | ||
248 | continue; | ||
249 | |||
250 | if (max(tile[i].addr, addr) < | ||
251 | min(tile[i].addr + tile[i].size, addr + size)) | ||
252 | /* Kill an intersecting tile region. */ | ||
253 | nv10_mem_set_region_tiling(dev, i, 0, 0, 0); | ||
254 | |||
255 | if (pitch && !found) { | ||
256 | /* Free tile region. */ | ||
257 | nv10_mem_set_region_tiling(dev, i, addr, size, pitch); | ||
258 | found = &tile[i]; | ||
259 | } | ||
260 | } | ||
261 | |||
262 | spin_unlock(&dev_priv->tile.lock); | ||
263 | |||
264 | return found; | ||
265 | } | ||
266 | |||
267 | void | ||
268 | nv10_mem_expire_tiling(struct drm_device *dev, struct nouveau_tile_reg *tile, | ||
269 | struct nouveau_fence *fence) | ||
270 | { | ||
271 | if (fence) { | ||
272 | /* Mark it as pending. */ | ||
273 | tile->fence = fence; | ||
274 | nouveau_fence_ref(fence); | ||
275 | } | ||
276 | |||
277 | tile->used = false; | ||
278 | } | ||
279 | |||
280 | /* | ||
195 | * NV50 VM helpers | 281 | * NV50 VM helpers |
196 | */ | 282 | */ |
197 | int | 283 | int |
@@ -199,53 +285,50 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, | |||
199 | uint32_t flags, uint64_t phys) | 285 | uint32_t flags, uint64_t phys) |
200 | { | 286 | { |
201 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 287 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
202 | struct nouveau_gpuobj **pgt; | 288 | struct nouveau_gpuobj *pgt; |
203 | unsigned psz, pfl, pages; | 289 | unsigned block; |
204 | 290 | int i; | |
205 | if (virt >= dev_priv->vm_gart_base && | 291 | |
206 | (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) { | 292 | virt = ((virt - dev_priv->vm_vram_base) >> 16) << 1; |
207 | psz = 12; | 293 | size = (size >> 16) << 1; |
208 | pgt = &dev_priv->gart_info.sg_ctxdma; | 294 | |
209 | pfl = 0x21; | 295 | phys |= ((uint64_t)flags << 32); |
210 | virt -= dev_priv->vm_gart_base; | 296 | phys |= 1; |
211 | } else | 297 | if (dev_priv->vram_sys_base) { |
212 | if (virt >= dev_priv->vm_vram_base && | 298 | phys += dev_priv->vram_sys_base; |
213 | (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) { | 299 | phys |= 0x30; |
214 | psz = 16; | ||
215 | pgt = dev_priv->vm_vram_pt; | ||
216 | pfl = 0x01; | ||
217 | virt -= dev_priv->vm_vram_base; | ||
218 | } else { | ||
219 | NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n", | ||
220 | virt, virt + size - 1); | ||
221 | return -EINVAL; | ||
222 | } | 300 | } |
223 | 301 | ||
224 | pages = size >> psz; | ||
225 | |||
226 | dev_priv->engine.instmem.prepare_access(dev, true); | 302 | dev_priv->engine.instmem.prepare_access(dev, true); |
227 | if (flags & 0x80000000) { | 303 | while (size) { |
228 | while (pages--) { | 304 | unsigned offset_h = upper_32_bits(phys); |
229 | struct nouveau_gpuobj *pt = pgt[virt >> 29]; | 305 | unsigned offset_l = lower_32_bits(phys); |
230 | unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1; | 306 | unsigned pte, end; |
307 | |||
308 | for (i = 7; i >= 0; i--) { | ||
309 | block = 1 << (i + 1); | ||
310 | if (size >= block && !(virt & (block - 1))) | ||
311 | break; | ||
312 | } | ||
313 | offset_l |= (i << 7); | ||
231 | 314 | ||
232 | nv_wo32(dev, pt, pte++, 0x00000000); | 315 | phys += block << 15; |
233 | nv_wo32(dev, pt, pte++, 0x00000000); | 316 | size -= block; |
234 | 317 | ||
235 | virt += (1 << psz); | 318 | while (block) { |
236 | } | 319 | pgt = dev_priv->vm_vram_pt[virt >> 14]; |
237 | } else { | 320 | pte = virt & 0x3ffe; |
238 | while (pages--) { | ||
239 | struct nouveau_gpuobj *pt = pgt[virt >> 29]; | ||
240 | unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1; | ||
241 | unsigned offset_h = upper_32_bits(phys) & 0xff; | ||
242 | unsigned offset_l = lower_32_bits(phys); | ||
243 | 321 | ||
244 | nv_wo32(dev, pt, pte++, offset_l | pfl); | 322 | end = pte + block; |
245 | nv_wo32(dev, pt, pte++, offset_h | flags); | 323 | if (end > 16384) |
324 | end = 16384; | ||
325 | block -= (end - pte); | ||
326 | virt += (end - pte); | ||
246 | 327 | ||
247 | phys += (1 << psz); | 328 | while (pte < end) { |
248 | virt += (1 << psz); | 329 | nv_wo32(dev, pgt, pte++, offset_l); |
330 | nv_wo32(dev, pgt, pte++, offset_h); | ||
331 | } | ||
249 | } | 332 | } |
250 | } | 333 | } |
251 | dev_priv->engine.instmem.finish_access(dev); | 334 | dev_priv->engine.instmem.finish_access(dev); |
@@ -270,7 +353,41 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size, | |||
270 | void | 353 | void |
271 | nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) | 354 | nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size) |
272 | { | 355 | { |
273 | nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0); | 356 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
357 | struct nouveau_gpuobj *pgt; | ||
358 | unsigned pages, pte, end; | ||
359 | |||
360 | virt -= dev_priv->vm_vram_base; | ||
361 | pages = (size >> 16) << 1; | ||
362 | |||
363 | dev_priv->engine.instmem.prepare_access(dev, true); | ||
364 | while (pages) { | ||
365 | pgt = dev_priv->vm_vram_pt[virt >> 29]; | ||
366 | pte = (virt & 0x1ffe0000ULL) >> 15; | ||
367 | |||
368 | end = pte + pages; | ||
369 | if (end > 16384) | ||
370 | end = 16384; | ||
371 | pages -= (end - pte); | ||
372 | virt += (end - pte) << 15; | ||
373 | |||
374 | while (pte < end) | ||
375 | nv_wo32(dev, pgt, pte++, 0); | ||
376 | } | ||
377 | dev_priv->engine.instmem.finish_access(dev); | ||
378 | |||
379 | nv_wr32(dev, 0x100c80, 0x00050001); | ||
380 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
381 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
382 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); | ||
383 | return; | ||
384 | } | ||
385 | |||
386 | nv_wr32(dev, 0x100c80, 0x00000001); | ||
387 | if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) { | ||
388 | NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n"); | ||
389 | NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80)); | ||
390 | } | ||
274 | } | 391 | } |
275 | 392 | ||
276 | /* | 393 | /* |
@@ -297,9 +414,8 @@ void nouveau_mem_close(struct drm_device *dev) | |||
297 | { | 414 | { |
298 | struct drm_nouveau_private *dev_priv = dev->dev_private; | 415 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
299 | 416 | ||
300 | if (dev_priv->ttm.bdev.man[TTM_PL_PRIV0].has_type) | 417 | nouveau_bo_unpin(dev_priv->vga_ram); |
301 | ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_PRIV0); | 418 | nouveau_bo_ref(NULL, &dev_priv->vga_ram); |
302 | ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM); | ||
303 | 419 | ||
304 | ttm_bo_device_release(&dev_priv->ttm.bdev); | 420 | ttm_bo_device_release(&dev_priv->ttm.bdev); |
305 | 421 | ||
@@ -513,6 +629,7 @@ nouveau_mem_init(struct drm_device *dev) | |||
513 | 629 | ||
514 | INIT_LIST_HEAD(&dev_priv->ttm.bo_list); | 630 | INIT_LIST_HEAD(&dev_priv->ttm.bo_list); |
515 | spin_lock_init(&dev_priv->ttm.bo_list_lock); | 631 | spin_lock_init(&dev_priv->ttm.bo_list_lock); |
632 | spin_lock_init(&dev_priv->tile.lock); | ||
516 | 633 | ||
517 | dev_priv->fb_available_size = nouveau_mem_fb_amount(dev); | 634 | dev_priv->fb_available_size = nouveau_mem_fb_amount(dev); |
518 | 635 | ||
@@ -535,6 +652,15 @@ nouveau_mem_init(struct drm_device *dev) | |||
535 | return ret; | 652 | return ret; |
536 | } | 653 | } |
537 | 654 | ||
655 | ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM, | ||
656 | 0, 0, true, true, &dev_priv->vga_ram); | ||
657 | if (ret == 0) | ||
658 | ret = nouveau_bo_pin(dev_priv->vga_ram, TTM_PL_FLAG_VRAM); | ||
659 | if (ret) { | ||
660 | NV_WARN(dev, "failed to reserve VGA memory\n"); | ||
661 | nouveau_bo_ref(NULL, &dev_priv->vga_ram); | ||
662 | } | ||
663 | |||
538 | /* GART */ | 664 | /* GART */ |
539 | #if !defined(__powerpc__) && !defined(__ia64__) | 665 | #if !defined(__powerpc__) && !defined(__ia64__) |
540 | if (drm_device_is_agp(dev) && dev->agp) { | 666 | if (drm_device_is_agp(dev) && dev->agp) { |
@@ -566,6 +692,7 @@ nouveau_mem_init(struct drm_device *dev) | |||
566 | dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1), | 692 | dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1), |
567 | drm_get_resource_len(dev, 1), | 693 | drm_get_resource_len(dev, 1), |
568 | DRM_MTRR_WC); | 694 | DRM_MTRR_WC); |
695 | |||
569 | return 0; | 696 | return 0; |
570 | } | 697 | } |
571 | 698 | ||