1 | /* |
2 | * Copyright 2017 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 | #include <drm/ttm/ttm_tt.h> |
23 | |
24 | #include "nouveau_mem.h" |
25 | #include "nouveau_drv.h" |
26 | #include "nouveau_bo.h" |
27 | |
28 | |
29 | #include <nvif/class.h> |
30 | #include <nvif/if000a.h> |
31 | #include <nvif/if500b.h> |
32 | #include <nvif/if500d.h> |
33 | #include <nvif/if900b.h> |
34 | #include <nvif/if900d.h> |
35 | |
36 | int |
37 | nouveau_mem_map(struct nouveau_mem *mem, |
38 | struct nvif_vmm *vmm, struct nvif_vma *vma) |
39 | { |
40 | union { |
41 | struct nv50_vmm_map_v0 nv50; |
42 | struct gf100_vmm_map_v0 gf100; |
43 | } args; |
44 | u32 argc = 0; |
45 | |
46 | switch (vmm->object.oclass) { |
47 | case NVIF_CLASS_VMM_NV04: |
48 | break; |
49 | case NVIF_CLASS_VMM_NV50: |
50 | args.nv50.version = 0; |
51 | args.nv50.ro = 0; |
52 | args.nv50.priv = 0; |
53 | args.nv50.kind = mem->kind; |
54 | args.nv50.comp = mem->comp; |
55 | argc = sizeof(args.nv50); |
56 | break; |
57 | case NVIF_CLASS_VMM_GF100: |
58 | case NVIF_CLASS_VMM_GM200: |
59 | case NVIF_CLASS_VMM_GP100: |
60 | args.gf100.version = 0; |
61 | if (mem->mem.type & NVIF_MEM_VRAM) |
62 | args.gf100.vol = 0; |
63 | else |
64 | args.gf100.vol = 1; |
65 | args.gf100.ro = 0; |
66 | args.gf100.priv = 0; |
67 | args.gf100.kind = mem->kind; |
68 | argc = sizeof(args.gf100); |
69 | break; |
70 | default: |
71 | WARN_ON(1); |
72 | return -ENOSYS; |
73 | } |
74 | |
75 | return nvif_vmm_map(vmm, vma->addr, mem->mem.size, &args, argc, &mem->mem, 0); |
76 | } |
77 | |
78 | void |
79 | nouveau_mem_fini(struct nouveau_mem *mem) |
80 | { |
81 | nvif_vmm_put(&mem->cli->drm->client.vmm.vmm, &mem->vma[1]); |
82 | nvif_vmm_put(&mem->cli->drm->client.vmm.vmm, &mem->vma[0]); |
83 | mutex_lock(&mem->cli->drm->master.lock); |
84 | nvif_mem_dtor(&mem->mem); |
85 | mutex_unlock(lock: &mem->cli->drm->master.lock); |
86 | } |
87 | |
88 | int |
89 | nouveau_mem_host(struct ttm_resource *reg, struct ttm_tt *tt) |
90 | { |
91 | struct nouveau_mem *mem = nouveau_mem(reg); |
92 | struct nouveau_cli *cli = mem->cli; |
93 | struct nouveau_drm *drm = cli->drm; |
94 | struct nvif_mmu *mmu = &cli->mmu; |
95 | struct nvif_mem_ram_v0 args = {}; |
96 | u8 type; |
97 | int ret; |
98 | |
99 | if (!nouveau_drm_use_coherent_gpu_mapping(drm)) |
100 | type = drm->ttm.type_ncoh[!!mem->kind]; |
101 | else |
102 | type = drm->ttm.type_host[0]; |
103 | |
104 | if (mem->kind && !(mmu->type[type].type & NVIF_MEM_KIND)) |
105 | mem->comp = mem->kind = 0; |
106 | if (mem->comp && !(mmu->type[type].type & NVIF_MEM_COMP)) { |
107 | if (mmu->object.oclass >= NVIF_CLASS_MMU_GF100) |
108 | mem->kind = mmu->kind[mem->kind]; |
109 | mem->comp = 0; |
110 | } |
111 | |
112 | if (tt->sg) |
113 | args.sgl = tt->sg->sgl; |
114 | else |
115 | args.dma = tt->dma_address; |
116 | |
117 | mutex_lock(&drm->master.lock); |
118 | ret = nvif_mem_ctor_type(mmu, "ttmHostMem" , cli->mem->oclass, type, PAGE_SHIFT, |
119 | reg->size, |
120 | &args, sizeof(args), &mem->mem); |
121 | mutex_unlock(lock: &drm->master.lock); |
122 | return ret; |
123 | } |
124 | |
125 | int |
126 | nouveau_mem_vram(struct ttm_resource *reg, bool contig, u8 page) |
127 | { |
128 | struct nouveau_mem *mem = nouveau_mem(reg); |
129 | struct nouveau_cli *cli = mem->cli; |
130 | struct nouveau_drm *drm = cli->drm; |
131 | struct nvif_mmu *mmu = &cli->mmu; |
132 | u64 size = ALIGN(reg->size, 1 << page); |
133 | int ret; |
134 | |
135 | mutex_lock(&drm->master.lock); |
136 | switch (cli->mem->oclass) { |
137 | case NVIF_CLASS_MEM_GF100: |
138 | ret = nvif_mem_ctor_type(mmu, "ttmVram" , cli->mem->oclass, |
139 | drm->ttm.type_vram, page, size, |
140 | &(struct gf100_mem_v0) { |
141 | .contig = contig, |
142 | }, sizeof(struct gf100_mem_v0), |
143 | &mem->mem); |
144 | break; |
145 | case NVIF_CLASS_MEM_NV50: |
146 | ret = nvif_mem_ctor_type(mmu, "ttmVram" , cli->mem->oclass, |
147 | drm->ttm.type_vram, page, size, |
148 | &(struct nv50_mem_v0) { |
149 | .bankswz = mmu->kind[mem->kind] == 2, |
150 | .contig = contig, |
151 | }, sizeof(struct nv50_mem_v0), |
152 | &mem->mem); |
153 | break; |
154 | default: |
155 | ret = -ENOSYS; |
156 | WARN_ON(1); |
157 | break; |
158 | } |
159 | mutex_unlock(lock: &drm->master.lock); |
160 | |
161 | reg->start = mem->mem.addr >> PAGE_SHIFT; |
162 | return ret; |
163 | } |
164 | |
165 | void |
166 | nouveau_mem_del(struct ttm_resource_manager *man, struct ttm_resource *reg) |
167 | { |
168 | struct nouveau_mem *mem = nouveau_mem(reg); |
169 | |
170 | nouveau_mem_fini(mem); |
171 | ttm_resource_fini(man, res: reg); |
172 | kfree(objp: mem); |
173 | } |
174 | |
175 | int |
176 | nouveau_mem_new(struct nouveau_cli *cli, u8 kind, u8 comp, |
177 | struct ttm_resource **res) |
178 | { |
179 | struct nouveau_mem *mem; |
180 | |
181 | if (!(mem = kzalloc(size: sizeof(*mem), GFP_KERNEL))) |
182 | return -ENOMEM; |
183 | |
184 | mem->cli = cli; |
185 | mem->kind = kind; |
186 | mem->comp = comp; |
187 | |
188 | *res = &mem->base; |
189 | return 0; |
190 | } |
191 | |
192 | bool |
193 | nouveau_mem_intersects(struct ttm_resource *res, |
194 | const struct ttm_place *place, |
195 | size_t size) |
196 | { |
197 | u32 num_pages = PFN_UP(size); |
198 | |
199 | /* Don't evict BOs outside of the requested placement range */ |
200 | if (place->fpfn >= (res->start + num_pages) || |
201 | (place->lpfn && place->lpfn <= res->start)) |
202 | return false; |
203 | |
204 | return true; |
205 | } |
206 | |
207 | bool |
208 | nouveau_mem_compatible(struct ttm_resource *res, |
209 | const struct ttm_place *place, |
210 | size_t size) |
211 | { |
212 | u32 num_pages = PFN_UP(size); |
213 | |
214 | if (res->start < place->fpfn || |
215 | (place->lpfn && (res->start + num_pages) > place->lpfn)) |
216 | return false; |
217 | |
218 | return true; |
219 | } |
220 | |