1 #include <linux/pagemap.h> 2 #include <linux/slab.h> 3 4 #include "nouveau_drv.h" 5 #include "nouveau_ttm.h" 6 7 struct nouveau_sgdma_be { 8 /* this has to be the first field so populate/unpopulated in 9 * nouve_bo.c works properly, otherwise have to move them here 10 */ 11 struct ttm_dma_tt ttm; 12 struct nvkm_mem *node; 13 }; 14 15 static void 16 nouveau_sgdma_destroy(struct ttm_tt *ttm) 17 { 18 struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; 19 20 if (ttm) { 21 ttm_dma_tt_fini(&nvbe->ttm); 22 kfree(nvbe); 23 } 24 } 25 26 static int 27 nv04_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *reg) 28 { 29 struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; 30 struct nvkm_mem *node = reg->mm_node; 31 32 if (ttm->sg) { 33 node->sg = ttm->sg; 34 node->pages = NULL; 35 } else { 36 node->sg = NULL; 37 node->pages = nvbe->ttm.dma_address; 38 } 39 node->size = (reg->num_pages << PAGE_SHIFT) >> 12; 40 41 nvkm_vm_map(&node->vma[0], node); 42 nvbe->node = node; 43 return 0; 44 } 45 46 static int 47 nv04_sgdma_unbind(struct ttm_tt *ttm) 48 { 49 struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; 50 nvkm_vm_unmap(&nvbe->node->vma[0]); 51 return 0; 52 } 53 54 static struct ttm_backend_func nv04_sgdma_backend = { 55 .bind = nv04_sgdma_bind, 56 .unbind = nv04_sgdma_unbind, 57 .destroy = nouveau_sgdma_destroy 58 }; 59 60 static int 61 nv50_sgdma_bind(struct ttm_tt *ttm, struct ttm_mem_reg *reg) 62 { 63 struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)ttm; 64 struct nvkm_mem *node = reg->mm_node; 65 66 /* noop: bound in move_notify() */ 67 if (ttm->sg) { 68 node->sg = ttm->sg; 69 node->pages = NULL; 70 } else { 71 node->sg = NULL; 72 node->pages = nvbe->ttm.dma_address; 73 } 74 node->size = (reg->num_pages << PAGE_SHIFT) >> 12; 75 return 0; 76 } 77 78 static int 79 nv50_sgdma_unbind(struct ttm_tt *ttm) 80 { 81 /* noop: unbound in move_notify() */ 82 return 0; 83 } 84 85 static struct ttm_backend_func nv50_sgdma_backend = { 86 .bind = nv50_sgdma_bind, 87 .unbind = nv50_sgdma_unbind, 88 .destroy = nouveau_sgdma_destroy 89 }; 90 91 struct ttm_tt * 92 nouveau_sgdma_create_ttm(struct ttm_bo_device *bdev, 93 unsigned long size, uint32_t page_flags, 94 struct page *dummy_read_page) 95 { 96 struct nouveau_drm *drm = nouveau_bdev(bdev); 97 struct nouveau_sgdma_be *nvbe; 98 99 nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL); 100 if (!nvbe) 101 return NULL; 102 103 if (drm->client.device.info.family < NV_DEVICE_INFO_V0_TESLA) 104 nvbe->ttm.ttm.func = &nv04_sgdma_backend; 105 else 106 nvbe->ttm.ttm.func = &nv50_sgdma_backend; 107 108 if (ttm_dma_tt_init(&nvbe->ttm, bdev, size, page_flags, dummy_read_page)) 109 /* 110 * A failing ttm_dma_tt_init() will call ttm_tt_destroy() 111 * and thus our nouveau_sgdma_destroy() hook, so we don't need 112 * to free nvbe here. 113 */ 114 return NULL; 115 return &nvbe->ttm.ttm; 116 } 117