Home | History | Annotate | Line # | Download | only in amdgpu
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