amdgpu_gart.c revision 1.1.1.2 1 /* $NetBSD: amdgpu_gart.c,v 1.1.1.2 2021/12/18 20:11:06 riastradh Exp $ */
2
3 /*
4 * Copyright 2008 Advanced Micro Devices, Inc.
5 * Copyright 2008 Red Hat Inc.
6 * Copyright 2009 Jerome Glisse.
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a
9 * copy of this software and associated documentation files (the "Software"),
10 * to deal in the Software without restriction, including without limitation
11 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 * and/or sell copies of the Software, and to permit persons to whom the
13 * Software is furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
22 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
23 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * Authors: Dave Airlie
27 * Alex Deucher
28 * Jerome Glisse
29 */
30
31 #include <sys/cdefs.h>
32 __KERNEL_RCSID(0, "$NetBSD: amdgpu_gart.c,v 1.1.1.2 2021/12/18 20:11:06 riastradh Exp $");
33
34 #include <linux/pci.h>
35 #include <linux/vmalloc.h>
36
37 #include <drm/amdgpu_drm.h>
38 #ifdef CONFIG_X86
39 #include <asm/set_memory.h>
40 #endif
41 #include "amdgpu.h"
42
43 /*
44 * GART
45 * The GART (Graphics Aperture Remapping Table) is an aperture
46 * in the GPU's address space. System pages can be mapped into
47 * the aperture and look like contiguous pages from the GPU's
48 * perspective. A page table maps the pages in the aperture
49 * to the actual backing pages in system memory.
50 *
51 * Radeon GPUs support both an internal GART, as described above,
52 * and AGP. AGP works similarly, but the GART table is configured
53 * and maintained by the northbridge rather than the driver.
54 * Radeon hw has a separate AGP aperture that is programmed to
55 * point to the AGP aperture provided by the northbridge and the
56 * requests are passed through to the northbridge aperture.
57 * Both AGP and internal GART can be used at the same time, however
58 * that is not currently supported by the driver.
59 *
60 * This file handles the common internal GART management.
61 */
62
63 /*
64 * Common GART table functions.
65 */
66
67 /**
68 * amdgpu_dummy_page_init - init dummy page used by the driver
69 *
70 * @adev: amdgpu_device pointer
71 *
72 * Allocate the dummy page used by the driver (all asics).
73 * This dummy page is used by the driver as a filler for gart entries
74 * when pages are taken out of the GART
75 * Returns 0 on sucess, -ENOMEM on failure.
76 */
77 static int amdgpu_gart_dummy_page_init(struct amdgpu_device *adev)
78 {
79 struct page *dummy_page = ttm_bo_glob.dummy_read_page;
80
81 if (adev->dummy_page_addr)
82 return 0;
83 adev->dummy_page_addr = pci_map_page(adev->pdev, dummy_page, 0,
84 PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
85 if (pci_dma_mapping_error(adev->pdev, adev->dummy_page_addr)) {
86 dev_err(&adev->pdev->dev, "Failed to DMA MAP the dummy page\n");
87 adev->dummy_page_addr = 0;
88 return -ENOMEM;
89 }
90 return 0;
91 }
92
93 /**
94 * amdgpu_dummy_page_fini - free dummy page used by the driver
95 *
96 * @adev: amdgpu_device pointer
97 *
98 * Frees the dummy page used by the driver (all asics).
99 */
100 static void amdgpu_gart_dummy_page_fini(struct amdgpu_device *adev)
101 {
102 if (!adev->dummy_page_addr)
103 return;
104 pci_unmap_page(adev->pdev, adev->dummy_page_addr,
105 PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
106 adev->dummy_page_addr = 0;
107 }
108
109 /**
110 * amdgpu_gart_table_vram_alloc - allocate vram for gart page table
111 *
112 * @adev: amdgpu_device pointer
113 *
114 * Allocate video memory for GART page table
115 * (pcie r4xx, r5xx+). These asics require the
116 * gart table to be in video memory.
117 * Returns 0 for success, error for failure.
118 */
119 int amdgpu_gart_table_vram_alloc(struct amdgpu_device *adev)
120 {
121 int r;
122
123 if (adev->gart.bo == NULL) {
124 struct amdgpu_bo_param bp;
125
126 memset(&bp, 0, sizeof(bp));
127 bp.size = adev->gart.table_size;
128 bp.byte_align = PAGE_SIZE;
129 bp.domain = AMDGPU_GEM_DOMAIN_VRAM;
130 bp.flags = AMDGPU_GEM_CREATE_CPU_ACCESS_REQUIRED |
131 AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS;
132 bp.type = ttm_bo_type_kernel;
133 bp.resv = NULL;
134 r = amdgpu_bo_create(adev, &bp, &adev->gart.bo);
135 if (r) {
136 return r;
137 }
138 }
139 return 0;
140 }
141
142 /**
143 * amdgpu_gart_table_vram_pin - pin gart page table in vram
144 *
145 * @adev: amdgpu_device pointer
146 *
147 * Pin the GART page table in vram so it will not be moved
148 * by the memory manager (pcie r4xx, r5xx+). These asics require the
149 * gart table to be in video memory.
150 * Returns 0 for success, error for failure.
151 */
152 int amdgpu_gart_table_vram_pin(struct amdgpu_device *adev)
153 {
154 int r;
155
156 r = amdgpu_bo_reserve(adev->gart.bo, false);
157 if (unlikely(r != 0))
158 return r;
159 r = amdgpu_bo_pin(adev->gart.bo, AMDGPU_GEM_DOMAIN_VRAM);
160 if (r) {
161 amdgpu_bo_unreserve(adev->gart.bo);
162 return r;
163 }
164 r = amdgpu_bo_kmap(adev->gart.bo, &adev->gart.ptr);
165 if (r)
166 amdgpu_bo_unpin(adev->gart.bo);
167 amdgpu_bo_unreserve(adev->gart.bo);
168 return r;
169 }
170
171 /**
172 * amdgpu_gart_table_vram_unpin - unpin gart page table in vram
173 *
174 * @adev: amdgpu_device pointer
175 *
176 * Unpin the GART page table in vram (pcie r4xx, r5xx+).
177 * These asics require the gart table to be in video memory.
178 */
179 void amdgpu_gart_table_vram_unpin(struct amdgpu_device *adev)
180 {
181 int r;
182
183 if (adev->gart.bo == NULL) {
184 return;
185 }
186 r = amdgpu_bo_reserve(adev->gart.bo, true);
187 if (likely(r == 0)) {
188 amdgpu_bo_kunmap(adev->gart.bo);
189 amdgpu_bo_unpin(adev->gart.bo);
190 amdgpu_bo_unreserve(adev->gart.bo);
191 adev->gart.ptr = NULL;
192 }
193 }
194
195 /**
196 * amdgpu_gart_table_vram_free - free gart page table vram
197 *
198 * @adev: amdgpu_device pointer
199 *
200 * Free the video memory used for the GART page table
201 * (pcie r4xx, r5xx+). These asics require the gart table to
202 * be in video memory.
203 */
204 void amdgpu_gart_table_vram_free(struct amdgpu_device *adev)
205 {
206 if (adev->gart.bo == NULL) {
207 return;
208 }
209 amdgpu_bo_unref(&adev->gart.bo);
210 }
211
212 /*
213 * Common gart functions.
214 */
215 /**
216 * amdgpu_gart_unbind - unbind pages from the gart page table
217 *
218 * @adev: amdgpu_device pointer
219 * @offset: offset into the GPU's gart aperture
220 * @pages: number of pages to unbind
221 *
222 * Unbinds the requested pages from the gart page table and
223 * replaces them with the dummy page (all asics).
224 * Returns 0 for success, -EINVAL for failure.
225 */
226 int amdgpu_gart_unbind(struct amdgpu_device *adev, uint64_t offset,
227 int pages)
228 {
229 unsigned t;
230 unsigned p;
231 int i, j;
232 u64 page_base;
233 /* Starting from VEGA10, system bit must be 0 to mean invalid. */
234 uint64_t flags = 0;
235
236 if (!adev->gart.ready) {
237 WARN(1, "trying to unbind memory from uninitialized GART !\n");
238 return -EINVAL;
239 }
240
241 t = offset / AMDGPU_GPU_PAGE_SIZE;
242 p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
243 for (i = 0; i < pages; i++, p++) {
244 #ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
245 adev->gart.pages[p] = NULL;
246 #endif
247 page_base = adev->dummy_page_addr;
248 if (!adev->gart.ptr)
249 continue;
250
251 for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) {
252 amdgpu_gmc_set_pte_pde(adev, adev->gart.ptr,
253 t, page_base, flags);
254 page_base += AMDGPU_GPU_PAGE_SIZE;
255 }
256 }
257 mb();
258 amdgpu_asic_flush_hdp(adev, NULL);
259 for (i = 0; i < adev->num_vmhubs; i++)
260 amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);
261
262 return 0;
263 }
264
265 /**
266 * amdgpu_gart_map - map dma_addresses into GART entries
267 *
268 * @adev: amdgpu_device pointer
269 * @offset: offset into the GPU's gart aperture
270 * @pages: number of pages to bind
271 * @dma_addr: DMA addresses of pages
272 * @flags: page table entry flags
273 * @dst: CPU address of the gart table
274 *
275 * Map the dma_addresses into GART entries (all asics).
276 * Returns 0 for success, -EINVAL for failure.
277 */
278 int amdgpu_gart_map(struct amdgpu_device *adev, uint64_t offset,
279 int pages, dma_addr_t *dma_addr, uint64_t flags,
280 void *dst)
281 {
282 uint64_t page_base;
283 unsigned i, j, t;
284
285 if (!adev->gart.ready) {
286 WARN(1, "trying to bind memory to uninitialized GART !\n");
287 return -EINVAL;
288 }
289
290 t = offset / AMDGPU_GPU_PAGE_SIZE;
291
292 for (i = 0; i < pages; i++) {
293 page_base = dma_addr[i];
294 for (j = 0; j < AMDGPU_GPU_PAGES_IN_CPU_PAGE; j++, t++) {
295 amdgpu_gmc_set_pte_pde(adev, dst, t, page_base, flags);
296 page_base += AMDGPU_GPU_PAGE_SIZE;
297 }
298 }
299 return 0;
300 }
301
302 /**
303 * amdgpu_gart_bind - bind pages into the gart page table
304 *
305 * @adev: amdgpu_device pointer
306 * @offset: offset into the GPU's gart aperture
307 * @pages: number of pages to bind
308 * @pagelist: pages to bind
309 * @dma_addr: DMA addresses of pages
310 * @flags: page table entry flags
311 *
312 * Binds the requested pages to the gart page table
313 * (all asics).
314 * Returns 0 for success, -EINVAL for failure.
315 */
316 int amdgpu_gart_bind(struct amdgpu_device *adev, uint64_t offset,
317 int pages, struct page **pagelist, dma_addr_t *dma_addr,
318 uint64_t flags)
319 {
320 #ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
321 unsigned t,p;
322 #endif
323 int r, i;
324
325 if (!adev->gart.ready) {
326 WARN(1, "trying to bind memory to uninitialized GART !\n");
327 return -EINVAL;
328 }
329
330 #ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
331 t = offset / AMDGPU_GPU_PAGE_SIZE;
332 p = t / AMDGPU_GPU_PAGES_IN_CPU_PAGE;
333 for (i = 0; i < pages; i++, p++)
334 adev->gart.pages[p] = pagelist ? pagelist[i] : NULL;
335 #endif
336
337 if (!adev->gart.ptr)
338 return 0;
339
340 r = amdgpu_gart_map(adev, offset, pages, dma_addr, flags,
341 adev->gart.ptr);
342 if (r)
343 return r;
344
345 mb();
346 amdgpu_asic_flush_hdp(adev, NULL);
347 for (i = 0; i < adev->num_vmhubs; i++)
348 amdgpu_gmc_flush_gpu_tlb(adev, 0, i, 0);
349 return 0;
350 }
351
352 /**
353 * amdgpu_gart_init - init the driver info for managing the gart
354 *
355 * @adev: amdgpu_device pointer
356 *
357 * Allocate the dummy page and init the gart driver info (all asics).
358 * Returns 0 for success, error for failure.
359 */
360 int amdgpu_gart_init(struct amdgpu_device *adev)
361 {
362 int r;
363
364 if (adev->dummy_page_addr)
365 return 0;
366
367 /* We need PAGE_SIZE >= AMDGPU_GPU_PAGE_SIZE */
368 if (PAGE_SIZE < AMDGPU_GPU_PAGE_SIZE) {
369 DRM_ERROR("Page size is smaller than GPU page size!\n");
370 return -EINVAL;
371 }
372 r = amdgpu_gart_dummy_page_init(adev);
373 if (r)
374 return r;
375 /* Compute table size */
376 adev->gart.num_cpu_pages = adev->gmc.gart_size / PAGE_SIZE;
377 adev->gart.num_gpu_pages = adev->gmc.gart_size / AMDGPU_GPU_PAGE_SIZE;
378 DRM_INFO("GART: num cpu pages %u, num gpu pages %u\n",
379 adev->gart.num_cpu_pages, adev->gart.num_gpu_pages);
380
381 #ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
382 /* Allocate pages table */
383 adev->gart.pages = vzalloc(array_size(sizeof(void *),
384 adev->gart.num_cpu_pages));
385 if (adev->gart.pages == NULL)
386 return -ENOMEM;
387 #endif
388
389 return 0;
390 }
391
392 /**
393 * amdgpu_gart_fini - tear down the driver info for managing the gart
394 *
395 * @adev: amdgpu_device pointer
396 *
397 * Tear down the gart driver info and free the dummy page (all asics).
398 */
399 void amdgpu_gart_fini(struct amdgpu_device *adev)
400 {
401 #ifdef CONFIG_DRM_AMDGPU_GART_DEBUGFS
402 vfree(adev->gart.pages);
403 adev->gart.pages = NULL;
404 #endif
405 amdgpu_gart_dummy_page_fini(adev);
406 }
407