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_bo *bo = container_of(tbo, struct amdgpu_bo, tbo);
976a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
986a7f76e7SChristian König 	struct drm_mm *mm = &mgr->mm;
996a7f76e7SChristian König 	struct drm_mm_node *nodes;
1006a7f76e7SChristian König 	enum drm_mm_search_flags sflags = DRM_MM_SEARCH_DEFAULT;
1016a7f76e7SChristian König 	enum drm_mm_allocator_flags aflags = DRM_MM_CREATE_DEFAULT;
1026a7f76e7SChristian König 	unsigned long lpfn, num_nodes, pages_per_node, pages_left;
1036a7f76e7SChristian König 	unsigned i;
1046a7f76e7SChristian König 	int r;
1056a7f76e7SChristian König 
1066a7f76e7SChristian König 	lpfn = place->lpfn;
1076a7f76e7SChristian König 	if (!lpfn)
1086a7f76e7SChristian König 		lpfn = man->size;
1096a7f76e7SChristian König 
1106a7f76e7SChristian König 	if (bo->flags & AMDGPU_GEM_CREATE_VRAM_CONTIGUOUS ||
1115befb22aSChristian König 	    place->lpfn || amdgpu_vram_page_split == -1) {
1126a7f76e7SChristian König 		pages_per_node = ~0ul;
1136a7f76e7SChristian König 		num_nodes = 1;
1146a7f76e7SChristian König 	} else {
1156a7f76e7SChristian König 		pages_per_node = max((uint32_t)amdgpu_vram_page_split,
1166a7f76e7SChristian König 				     mem->page_alignment);
1176a7f76e7SChristian König 		num_nodes = DIV_ROUND_UP(mem->num_pages, pages_per_node);
1186a7f76e7SChristian König 	}
1196a7f76e7SChristian König 
1206a7f76e7SChristian König 	nodes = kcalloc(num_nodes, sizeof(*nodes), GFP_KERNEL);
1216a7f76e7SChristian König 	if (!nodes)
1226a7f76e7SChristian König 		return -ENOMEM;
1236a7f76e7SChristian König 
1246a7f76e7SChristian König 	if (place->flags & TTM_PL_FLAG_TOPDOWN) {
1256a7f76e7SChristian König 		sflags = DRM_MM_SEARCH_BELOW;
1266a7f76e7SChristian König 		aflags = DRM_MM_CREATE_TOP;
1276a7f76e7SChristian König 	}
1286a7f76e7SChristian König 
1296a7f76e7SChristian König 	pages_left = mem->num_pages;
1306a7f76e7SChristian König 
1316a7f76e7SChristian König 	spin_lock(&mgr->lock);
1326a7f76e7SChristian König 	for (i = 0; i < num_nodes; ++i) {
1336a7f76e7SChristian König 		unsigned long pages = min(pages_left, pages_per_node);
1346a7f76e7SChristian König 		uint32_t alignment = mem->page_alignment;
1356a7f76e7SChristian König 
1366a7f76e7SChristian König 		if (pages == pages_per_node)
1376a7f76e7SChristian König 			alignment = pages_per_node;
1386a7f76e7SChristian König 		else
1396a7f76e7SChristian König 			sflags |= DRM_MM_SEARCH_BEST;
1406a7f76e7SChristian König 
1416a7f76e7SChristian König 		r = drm_mm_insert_node_in_range_generic(mm, &nodes[i], pages,
1426a7f76e7SChristian König 							alignment, 0,
1436a7f76e7SChristian König 							place->fpfn, lpfn,
1446a7f76e7SChristian König 							sflags, aflags);
1456a7f76e7SChristian König 		if (unlikely(r))
1466a7f76e7SChristian König 			goto error;
1476a7f76e7SChristian König 
1486a7f76e7SChristian König 		pages_left -= pages;
1496a7f76e7SChristian König 	}
1506a7f76e7SChristian König 	spin_unlock(&mgr->lock);
1516a7f76e7SChristian König 
1526a7f76e7SChristian König 	mem->start = num_nodes == 1 ? nodes[0].start : AMDGPU_BO_INVALID_OFFSET;
1536a7f76e7SChristian König 	mem->mm_node = nodes;
1546a7f76e7SChristian König 
1556a7f76e7SChristian König 	return 0;
1566a7f76e7SChristian König 
1576a7f76e7SChristian König error:
1586a7f76e7SChristian König 	while (i--)
1596a7f76e7SChristian König 		drm_mm_remove_node(&nodes[i]);
1606a7f76e7SChristian König 	spin_unlock(&mgr->lock);
1616a7f76e7SChristian König 
1626a7f76e7SChristian König 	kfree(nodes);
1636a7f76e7SChristian König 	return r == -ENOSPC ? 0 : r;
1646a7f76e7SChristian König }
1656a7f76e7SChristian König 
1666a7f76e7SChristian König /**
1676a7f76e7SChristian König  * amdgpu_vram_mgr_del - free ranges
1686a7f76e7SChristian König  *
1696a7f76e7SChristian König  * @man: TTM memory type manager
1706a7f76e7SChristian König  * @tbo: TTM BO we need this range for
1716a7f76e7SChristian König  * @place: placement flags and restrictions
1726a7f76e7SChristian König  * @mem: TTM memory object
1736a7f76e7SChristian König  *
1746a7f76e7SChristian König  * Free the allocated VRAM again.
1756a7f76e7SChristian König  */
1766a7f76e7SChristian König static void amdgpu_vram_mgr_del(struct ttm_mem_type_manager *man,
1776a7f76e7SChristian König 				struct ttm_mem_reg *mem)
1786a7f76e7SChristian König {
1796a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
1806a7f76e7SChristian König 	struct drm_mm_node *nodes = mem->mm_node;
1816a7f76e7SChristian König 	unsigned pages = mem->num_pages;
1826a7f76e7SChristian König 
1836a7f76e7SChristian König 	if (!mem->mm_node)
1846a7f76e7SChristian König 		return;
1856a7f76e7SChristian König 
1866a7f76e7SChristian König 	spin_lock(&mgr->lock);
1876a7f76e7SChristian König 	while (pages) {
1886a7f76e7SChristian König 		pages -= nodes->size;
1896a7f76e7SChristian König 		drm_mm_remove_node(nodes);
1906a7f76e7SChristian König 		++nodes;
1916a7f76e7SChristian König 	}
1926a7f76e7SChristian König 	spin_unlock(&mgr->lock);
1936a7f76e7SChristian König 
1946a7f76e7SChristian König 	kfree(mem->mm_node);
1956a7f76e7SChristian König 	mem->mm_node = NULL;
1966a7f76e7SChristian König }
1976a7f76e7SChristian König 
1986a7f76e7SChristian König /**
1996a7f76e7SChristian König  * amdgpu_vram_mgr_debug - dump VRAM table
2006a7f76e7SChristian König  *
2016a7f76e7SChristian König  * @man: TTM memory type manager
2026a7f76e7SChristian König  * @prefix: text prefix
2036a7f76e7SChristian König  *
2046a7f76e7SChristian König  * Dump the table content using printk.
2056a7f76e7SChristian König  */
2066a7f76e7SChristian König static void amdgpu_vram_mgr_debug(struct ttm_mem_type_manager *man,
2076a7f76e7SChristian König 				  const char *prefix)
2086a7f76e7SChristian König {
2096a7f76e7SChristian König 	struct amdgpu_vram_mgr *mgr = man->priv;
210b5c3714fSDaniel Vetter 	struct drm_printer p = drm_debug_printer(prefix);
2116a7f76e7SChristian König 
2126a7f76e7SChristian König 	spin_lock(&mgr->lock);
213b5c3714fSDaniel Vetter 	drm_mm_print(&mgr->mm, &p);
2146a7f76e7SChristian König 	spin_unlock(&mgr->lock);
2156a7f76e7SChristian König }
2166a7f76e7SChristian König 
2176a7f76e7SChristian König const struct ttm_mem_type_manager_func amdgpu_vram_mgr_func = {
2186a7f76e7SChristian König 	amdgpu_vram_mgr_init,
2196a7f76e7SChristian König 	amdgpu_vram_mgr_fini,
2206a7f76e7SChristian König 	amdgpu_vram_mgr_new,
2216a7f76e7SChristian König 	amdgpu_vram_mgr_del,
2226a7f76e7SChristian König 	amdgpu_vram_mgr_debug
2236a7f76e7SChristian König };
224