1 // SPDX-License-Identifier: MIT
2 /*
3  * Copyright © 2019 Intel Corporation
4  */
5 
6 #include "intel_memory_region.h"
7 #include "i915_drv.h"
8 #include "i915_ttm_buddy_manager.h"
9 
10 static const struct {
11 	u16 class;
12 	u16 instance;
13 } intel_region_map[] = {
14 	[INTEL_REGION_SMEM] = {
15 		.class = INTEL_MEMORY_SYSTEM,
16 		.instance = 0,
17 	},
18 	[INTEL_REGION_LMEM] = {
19 		.class = INTEL_MEMORY_LOCAL,
20 		.instance = 0,
21 	},
22 	[INTEL_REGION_STOLEN_SMEM] = {
23 		.class = INTEL_MEMORY_STOLEN_SYSTEM,
24 		.instance = 0,
25 	},
26 	[INTEL_REGION_STOLEN_LMEM] = {
27 		.class = INTEL_MEMORY_STOLEN_LOCAL,
28 		.instance = 0,
29 	},
30 };
31 
32 struct intel_memory_region *
33 intel_memory_region_lookup(struct drm_i915_private *i915,
34 			   u16 class, u16 instance)
35 {
36 	struct intel_memory_region *mr;
37 	int id;
38 
39 	/* XXX: consider maybe converting to an rb tree at some point */
40 	for_each_memory_region(mr, i915, id) {
41 		if (mr->type == class && mr->instance == instance)
42 			return mr;
43 	}
44 
45 	return NULL;
46 }
47 
48 struct intel_memory_region *
49 intel_memory_region_by_type(struct drm_i915_private *i915,
50 			    enum intel_memory_type mem_type)
51 {
52 	struct intel_memory_region *mr;
53 	int id;
54 
55 	for_each_memory_region(mr, i915, id)
56 		if (mr->type == mem_type)
57 			return mr;
58 
59 	return NULL;
60 }
61 
62 /**
63  * intel_memory_region_reserve - Reserve a memory range
64  * @mem: The region for which we want to reserve a range.
65  * @offset: Start of the range to reserve.
66  * @size: The size of the range to reserve.
67  *
68  * Return: 0 on success, negative error code on failure.
69  */
70 int intel_memory_region_reserve(struct intel_memory_region *mem,
71 				resource_size_t offset,
72 				resource_size_t size)
73 {
74 	struct ttm_resource_manager *man = mem->region_private;
75 
76 	GEM_BUG_ON(mem->is_range_manager);
77 
78 	return i915_ttm_buddy_man_reserve(man, offset, size);
79 }
80 
81 void intel_memory_region_debug(struct intel_memory_region *mr,
82 			       struct drm_printer *printer)
83 {
84 	drm_printf(printer, "%s: ", mr->name);
85 
86 	if (mr->region_private)
87 		ttm_resource_manager_debug(mr->region_private, printer);
88 	else
89 		drm_printf(printer, "total:%pa, available:%pa bytes\n",
90 			   &mr->total, &mr->avail);
91 }
92 
93 struct intel_memory_region *
94 intel_memory_region_create(struct drm_i915_private *i915,
95 			   resource_size_t start,
96 			   resource_size_t size,
97 			   resource_size_t min_page_size,
98 			   resource_size_t io_start,
99 			   u16 type,
100 			   u16 instance,
101 			   const struct intel_memory_region_ops *ops)
102 {
103 	struct intel_memory_region *mem;
104 	int err;
105 
106 	mem = kzalloc(sizeof(*mem), GFP_KERNEL);
107 	if (!mem)
108 		return ERR_PTR(-ENOMEM);
109 
110 	mem->i915 = i915;
111 	mem->region = (struct resource)DEFINE_RES_MEM(start, size);
112 	mem->io_start = io_start;
113 	mem->min_page_size = min_page_size;
114 	mem->ops = ops;
115 	mem->total = size;
116 	mem->avail = mem->total;
117 	mem->type = type;
118 	mem->instance = instance;
119 
120 	mutex_init(&mem->objects.lock);
121 	INIT_LIST_HEAD(&mem->objects.list);
122 
123 	if (ops->init) {
124 		err = ops->init(mem);
125 		if (err)
126 			goto err_free;
127 	}
128 
129 	kref_init(&mem->kref);
130 	return mem;
131 
132 err_free:
133 	kfree(mem);
134 	return ERR_PTR(err);
135 }
136 
137 void intel_memory_region_set_name(struct intel_memory_region *mem,
138 				  const char *fmt, ...)
139 {
140 	va_list ap;
141 
142 	va_start(ap, fmt);
143 	vsnprintf(mem->name, sizeof(mem->name), fmt, ap);
144 	va_end(ap);
145 }
146 
147 static void __intel_memory_region_destroy(struct kref *kref)
148 {
149 	struct intel_memory_region *mem =
150 		container_of(kref, typeof(*mem), kref);
151 
152 	if (mem->ops->release)
153 		mem->ops->release(mem);
154 
155 	mutex_destroy(&mem->objects.lock);
156 	kfree(mem);
157 }
158 
159 struct intel_memory_region *
160 intel_memory_region_get(struct intel_memory_region *mem)
161 {
162 	kref_get(&mem->kref);
163 	return mem;
164 }
165 
166 void intel_memory_region_put(struct intel_memory_region *mem)
167 {
168 	kref_put(&mem->kref, __intel_memory_region_destroy);
169 }
170 
171 /* Global memory region registration -- only slight layer inversions! */
172 
173 int intel_memory_regions_hw_probe(struct drm_i915_private *i915)
174 {
175 	int err, i;
176 
177 	for (i = 0; i < ARRAY_SIZE(i915->mm.regions); i++) {
178 		struct intel_memory_region *mem = ERR_PTR(-ENODEV);
179 		u16 type, instance;
180 
181 		if (!HAS_REGION(i915, BIT(i)))
182 			continue;
183 
184 		type = intel_region_map[i].class;
185 		instance = intel_region_map[i].instance;
186 		switch (type) {
187 		case INTEL_MEMORY_SYSTEM:
188 			if (IS_DGFX(i915))
189 				mem = i915_gem_ttm_system_setup(i915, type,
190 								instance);
191 			else
192 				mem = i915_gem_shmem_setup(i915, type,
193 							   instance);
194 			break;
195 		case INTEL_MEMORY_STOLEN_LOCAL:
196 			mem = i915_gem_stolen_lmem_setup(i915, type, instance);
197 			if (!IS_ERR(mem))
198 				i915->mm.stolen_region = mem;
199 			break;
200 		case INTEL_MEMORY_STOLEN_SYSTEM:
201 			mem = i915_gem_stolen_smem_setup(i915, type, instance);
202 			if (!IS_ERR(mem))
203 				i915->mm.stolen_region = mem;
204 			break;
205 		default:
206 			continue;
207 		}
208 
209 		if (IS_ERR(mem)) {
210 			err = PTR_ERR(mem);
211 			drm_err(&i915->drm,
212 				"Failed to setup region(%d) type=%d\n",
213 				err, type);
214 			goto out_cleanup;
215 		}
216 
217 		mem->id = i;
218 		i915->mm.regions[i] = mem;
219 	}
220 
221 	return 0;
222 
223 out_cleanup:
224 	intel_memory_regions_driver_release(i915);
225 	return err;
226 }
227 
228 void intel_memory_regions_driver_release(struct drm_i915_private *i915)
229 {
230 	int i;
231 
232 	for (i = 0; i < ARRAY_SIZE(i915->mm.regions); i++) {
233 		struct intel_memory_region *region =
234 			fetch_and_zero(&i915->mm.regions[i]);
235 
236 		if (region)
237 			intel_memory_region_put(region);
238 	}
239 }
240 
241 #if IS_ENABLED(CONFIG_DRM_I915_SELFTEST)
242 #include "selftests/intel_memory_region.c"
243 #include "selftests/mock_region.c"
244 #endif
245