16a7f76e7SChristian König /* 26a7f76e7SChristian König * Copyright 2016 Advanced Micro Devices, Inc. 36a7f76e7SChristian König * 46a7f76e7SChristian König * Permission is hereby granted, free of charge, to any person obtaining a 56a7f76e7SChristian König * copy of this software and associated documentation files (the "Software"), 66a7f76e7SChristian König * to deal in the Software without restriction, including without limitation 76a7f76e7SChristian König * the rights to use, copy, modify, merge, publish, distribute, sublicense, 86a7f76e7SChristian König * and/or sell copies of the Software, and to permit persons to whom the 96a7f76e7SChristian König * Software is furnished to do so, subject to the following conditions: 106a7f76e7SChristian König * 116a7f76e7SChristian König * The above copyright notice and this permission notice shall be included in 126a7f76e7SChristian König * all copies or substantial portions of the Software. 136a7f76e7SChristian König * 146a7f76e7SChristian König * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 156a7f76e7SChristian König * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 166a7f76e7SChristian König * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 176a7f76e7SChristian König * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR 186a7f76e7SChristian König * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 196a7f76e7SChristian König * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 206a7f76e7SChristian König * OTHER DEALINGS IN THE SOFTWARE. 216a7f76e7SChristian König * 226a7f76e7SChristian König * Authors: Christian König 236a7f76e7SChristian König */ 246a7f76e7SChristian König 256a7f76e7SChristian König #include <drm/drmP.h> 266a7f76e7SChristian König #include "amdgpu.h" 276a7f76e7SChristian König 286a7f76e7SChristian König struct amdgpu_vram_mgr { 296a7f76e7SChristian König struct drm_mm mm; 306a7f76e7SChristian König spinlock_t lock; 316a7f76e7SChristian König }; 326a7f76e7SChristian König 336a7f76e7SChristian König /** 346a7f76e7SChristian König * amdgpu_vram_mgr_init - init VRAM manager and DRM MM 356a7f76e7SChristian König * 366a7f76e7SChristian König * @man: TTM memory type manager 376a7f76e7SChristian König * @p_size: maximum size of VRAM 386a7f76e7SChristian König * 396a7f76e7SChristian König * Allocate and initialize the VRAM manager. 406a7f76e7SChristian König */ 416a7f76e7SChristian König static int amdgpu_vram_mgr_init(struct ttm_mem_type_manager *man, 426a7f76e7SChristian König unsigned long p_size) 436a7f76e7SChristian König { 446a7f76e7SChristian König struct amdgpu_vram_mgr *mgr; 456a7f76e7SChristian König 466a7f76e7SChristian König mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); 476a7f76e7SChristian König if (!mgr) 486a7f76e7SChristian König return -ENOMEM; 496a7f76e7SChristian König 506a7f76e7SChristian König drm_mm_init(&mgr->mm, 0, p_size); 516a7f76e7SChristian König spin_lock_init(&mgr->lock); 526a7f76e7SChristian König man->priv = mgr; 536a7f76e7SChristian König return 0; 546a7f76e7SChristian König } 556a7f76e7SChristian König 566a7f76e7SChristian König /** 576a7f76e7SChristian König * amdgpu_vram_mgr_fini - free and destroy VRAM manager 586a7f76e7SChristian König * 596a7f76e7SChristian König * @man: TTM memory type manager 606a7f76e7SChristian König * 616a7f76e7SChristian König * Destroy and free the VRAM manager, returns -EBUSY if ranges are still 626a7f76e7SChristian König * allocated inside it. 636a7f76e7SChristian König */ 646a7f76e7SChristian König static int amdgpu_vram_mgr_fini(struct ttm_mem_type_manager *man) 656a7f76e7SChristian König { 666a7f76e7SChristian König struct amdgpu_vram_mgr *mgr = man->priv; 676a7f76e7SChristian König 686a7f76e7SChristian König spin_lock(&mgr->lock); 696a7f76e7SChristian König if (!drm_mm_clean(&mgr->mm)) { 706a7f76e7SChristian König spin_unlock(&mgr->lock); 716a7f76e7SChristian König return -EBUSY; 726a7f76e7SChristian König } 736a7f76e7SChristian König 746a7f76e7SChristian König drm_mm_takedown(&mgr->mm); 756a7f76e7SChristian König spin_unlock(&mgr->lock); 766a7f76e7SChristian König kfree(mgr); 776a7f76e7SChristian König man->priv = NULL; 786a7f76e7SChristian König return 0; 796a7f76e7SChristian König } 806a7f76e7SChristian König 816a7f76e7SChristian König /** 826a7f76e7SChristian König * amdgpu_vram_mgr_new - allocate new ranges 836a7f76e7SChristian König * 846a7f76e7SChristian König * @man: TTM memory type manager 856a7f76e7SChristian König * @tbo: TTM BO we need this range for 866a7f76e7SChristian König * @place: placement flags and restrictions 876a7f76e7SChristian König * @mem: the resulting mem object 886a7f76e7SChristian König * 896a7f76e7SChristian König * Allocate VRAM for the given BO. 906a7f76e7SChristian König */ 916a7f76e7SChristian König static int amdgpu_vram_mgr_new(struct ttm_mem_type_manager *man, 926a7f76e7SChristian König struct ttm_buffer_object *tbo, 936a7f76e7SChristian König const struct ttm_place *place, 946a7f76e7SChristian König struct ttm_mem_reg *mem) 956a7f76e7SChristian König { 966a7f76e7SChristian König struct amdgpu_vram_mgr *mgr = man->priv; 976a7f76e7SChristian König struct drm_mm *mm = &mgr->mm; 986a7f76e7SChristian König struct drm_mm_node *nodes; 994e64e553SChris Wilson enum drm_mm_insert_mode mode; 1006a7f76e7SChristian König unsigned long lpfn, num_nodes, pages_per_node, pages_left; 1016a7f76e7SChristian König unsigned i; 1026a7f76e7SChristian König int r; 1036a7f76e7SChristian König 1046a7f76e7SChristian König lpfn = place->lpfn; 1056a7f76e7SChristian König if (!lpfn) 1066a7f76e7SChristian König lpfn = man->size; 1076a7f76e7SChristian König 10889bb5752SChristian König if (place->flags & TTM_PL_FLAG_CONTIGUOUS || 10989bb5752SChristian König amdgpu_vram_page_split == -1) { 1106a7f76e7SChristian König pages_per_node = ~0ul; 1116a7f76e7SChristian König num_nodes = 1; 1126a7f76e7SChristian König } else { 1136a7f76e7SChristian König pages_per_node = max((uint32_t)amdgpu_vram_page_split, 1146a7f76e7SChristian König mem->page_alignment); 1156a7f76e7SChristian König num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node); 1166a7f76e7SChristian König } 1176a7f76e7SChristian König 1186a7f76e7SChristian König nodes = kcalloc(num_nodes, sizeof(*nodes), GFP_KERNEL); 1196a7f76e7SChristian König if (!nodes) 1206a7f76e7SChristian König return -ENOMEM; 1216a7f76e7SChristian König 1224e64e553SChris Wilson mode = DRM_MM_INSERT_BEST; 1234e64e553SChris Wilson if (place->flags & TTM_PL_FLAG_TOPDOWN) 1244e64e553SChris Wilson mode = DRM_MM_INSERT_HIGH; 1256a7f76e7SChristian König 12689bb5752SChristian König mem->start = 0; 1276a7f76e7SChristian König pages_left = mem->num_pages; 1286a7f76e7SChristian König 1296a7f76e7SChristian König spin_lock(&mgr->lock); 1306a7f76e7SChristian König for (i = 0; i < num_nodes; ++i) { 1316a7f76e7SChristian König unsigned long pages = min(pages_left, pages_per_node); 1326a7f76e7SChristian König uint32_t alignment = mem->page_alignment; 13389bb5752SChristian König unsigned long start; 1346a7f76e7SChristian König 1356a7f76e7SChristian König if (pages == pages_per_node) 1366a7f76e7SChristian König alignment = pages_per_node; 1376a7f76e7SChristian König 1384e64e553SChris Wilson r = drm_mm_insert_node_in_range(mm, &nodes[i], 1394e64e553SChris Wilson pages, alignment, 0, 1406a7f76e7SChristian König place->fpfn, lpfn, 1414e64e553SChris Wilson mode); 1426a7f76e7SChristian König if (unlikely(r)) 1436a7f76e7SChristian König goto error; 1446a7f76e7SChristian König 14589bb5752SChristian König /* Calculate a virtual BO start address to easily check if 14689bb5752SChristian König * everything is CPU accessible. 14789bb5752SChristian König */ 14889bb5752SChristian König start = nodes[i].start + nodes[i].size; 14989bb5752SChristian König if (start > mem->num_pages) 15089bb5752SChristian König start -= mem->num_pages; 15189bb5752SChristian König else 15289bb5752SChristian König start = 0; 15389bb5752SChristian König mem->start = max(mem->start, start); 1546a7f76e7SChristian König pages_left -= pages; 1556a7f76e7SChristian König } 1566a7f76e7SChristian König spin_unlock(&mgr->lock); 1576a7f76e7SChristian König 1586a7f76e7SChristian König mem->mm_node = nodes; 1596a7f76e7SChristian König 1606a7f76e7SChristian König return 0; 1616a7f76e7SChristian König 1626a7f76e7SChristian König error: 1636a7f76e7SChristian König while (i--) 1646a7f76e7SChristian König drm_mm_remove_node(&nodes[i]); 1656a7f76e7SChristian König spin_unlock(&mgr->lock); 1666a7f76e7SChristian König 1676a7f76e7SChristian König kfree(nodes); 1686a7f76e7SChristian König return r == -ENOSPC ? 0 : r; 1696a7f76e7SChristian König } 1706a7f76e7SChristian König 1716a7f76e7SChristian König /** 1726a7f76e7SChristian König * amdgpu_vram_mgr_del - free ranges 1736a7f76e7SChristian König * 1746a7f76e7SChristian König * @man: TTM memory type manager 1756a7f76e7SChristian König * @tbo: TTM BO we need this range for 1766a7f76e7SChristian König * @place: placement flags and restrictions 1776a7f76e7SChristian König * @mem: TTM memory object 1786a7f76e7SChristian König * 1796a7f76e7SChristian König * Free the allocated VRAM again. 1806a7f76e7SChristian König */ 1816a7f76e7SChristian König static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man, 1826a7f76e7SChristian König struct ttm_mem_reg *mem) 1836a7f76e7SChristian König { 1846a7f76e7SChristian König struct amdgpu_vram_mgr *mgr = man->priv; 1856a7f76e7SChristian König struct drm_mm_node *nodes = mem->mm_node; 1866a7f76e7SChristian König unsigned pages = mem->num_pages; 1876a7f76e7SChristian König 1886a7f76e7SChristian König if (!mem->mm_node) 1896a7f76e7SChristian König return; 1906a7f76e7SChristian König 1916a7f76e7SChristian König spin_lock(&mgr->lock); 1926a7f76e7SChristian König while (pages) { 1936a7f76e7SChristian König pages -= nodes->size; 1946a7f76e7SChristian König drm_mm_remove_node(nodes); 1956a7f76e7SChristian König ++nodes; 1966a7f76e7SChristian König } 1976a7f76e7SChristian König spin_unlock(&mgr->lock); 1986a7f76e7SChristian König 1996a7f76e7SChristian König kfree(mem->mm_node); 2006a7f76e7SChristian König mem->mm_node = NULL; 2016a7f76e7SChristian König } 2026a7f76e7SChristian König 2036a7f76e7SChristian König /** 2046a7f76e7SChristian König * amdgpu_vram_mgr_debug - dump VRAM table 2056a7f76e7SChristian König * 2066a7f76e7SChristian König * @man: TTM memory type manager 2076a7f76e7SChristian König * @prefix: text prefix 2086a7f76e7SChristian König * 2096a7f76e7SChristian König * Dump the table content using printk. 2106a7f76e7SChristian König */ 2116a7f76e7SChristian König static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man, 2126a7f76e7SChristian König const char *prefix) 2136a7f76e7SChristian König { 2146a7f76e7SChristian König struct amdgpu_vram_mgr *mgr = man->priv; 215b5c3714fSDaniel Vetter struct drm_printer p = drm_debug_printer(prefix); 2166a7f76e7SChristian König 2176a7f76e7SChristian König spin_lock(&mgr->lock); 218b5c3714fSDaniel Vetter drm_mm_print(&mgr->mm, &p); 2196a7f76e7SChristian König spin_unlock(&mgr->lock); 2206a7f76e7SChristian König } 2216a7f76e7SChristian König 2226a7f76e7SChristian König const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = { 2236a7f76e7SChristian König amdgpu_vram_mgr_init, 2246a7f76e7SChristian König amdgpu_vram_mgr_fini, 2256a7f76e7SChristian König amdgpu_vram_mgr_new, 2266a7f76e7SChristian König amdgpu_vram_mgr_del, 2276a7f76e7SChristian König amdgpu_vram_mgr_debug 2286a7f76e7SChristian König }; 229