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