7a5d410c7ca061ed2631627a577d56cb51bdfa5e
[mesa.git] / src / freedreno / vulkan / tu_drm.c
1 /*
2 * Copyright © 2018 Google, Inc.
3 * Copyright © 2015 Intel Corporation
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice (including the next
13 * paragraph) shall be included in all copies or substantial portions of the
14 * Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 * DEALINGS IN THE SOFTWARE.
23 */
24
25 #include "tu_private.h"
26
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdint.h>
30 #include <sys/ioctl.h>
31 #include <sys/mman.h>
32 #include <xf86drm.h>
33
34 #include "drm-uapi/msm_drm.h"
35
36 static int
37 tu_drm_get_param(const struct tu_physical_device *dev,
38 uint32_t param,
39 uint64_t *value)
40 {
41 /* Technically this requires a pipe, but the kernel only supports one pipe
42 * anyway at the time of writing and most of these are clearly pipe
43 * independent. */
44 struct drm_msm_param req = {
45 .pipe = MSM_PIPE_3D0,
46 .param = param,
47 };
48
49 int ret = drmCommandWriteRead(dev->local_fd, DRM_MSM_GET_PARAM, &req,
50 sizeof(req));
51 if (ret)
52 return ret;
53
54 *value = req.value;
55
56 return 0;
57 }
58
59 static int
60 tu_drm_get_gpu_id(const struct tu_physical_device *dev, uint32_t *id)
61 {
62 uint64_t value;
63 int ret = tu_drm_get_param(dev, MSM_PARAM_GPU_ID, &value);
64 if (ret)
65 return ret;
66
67 *id = value;
68 return 0;
69 }
70
71 static int
72 tu_drm_get_gmem_size(const struct tu_physical_device *dev, uint32_t *size)
73 {
74 uint64_t value;
75 int ret = tu_drm_get_param(dev, MSM_PARAM_GMEM_SIZE, &value);
76 if (ret)
77 return ret;
78
79 *size = value;
80 return 0;
81 }
82
83 static int
84 tu_drm_get_gmem_base(const struct tu_physical_device *dev, uint64_t *base)
85 {
86 return tu_drm_get_param(dev, MSM_PARAM_GMEM_BASE, base);
87 }
88
89 int
90 tu_drm_submitqueue_new(const struct tu_device *dev,
91 int priority,
92 uint32_t *queue_id)
93 {
94 struct drm_msm_submitqueue req = {
95 .flags = 0,
96 .prio = priority,
97 };
98
99 int ret = drmCommandWriteRead(dev->physical_device->local_fd,
100 DRM_MSM_SUBMITQUEUE_NEW, &req, sizeof(req));
101 if (ret)
102 return ret;
103
104 *queue_id = req.id;
105 return 0;
106 }
107
108 void
109 tu_drm_submitqueue_close(const struct tu_device *dev, uint32_t queue_id)
110 {
111 drmCommandWrite(dev->physical_device->local_fd, DRM_MSM_SUBMITQUEUE_CLOSE,
112 &queue_id, sizeof(uint32_t));
113 }
114
115 static void
116 tu_gem_close(const struct tu_device *dev, uint32_t gem_handle)
117 {
118 struct drm_gem_close req = {
119 .handle = gem_handle,
120 };
121
122 drmIoctl(dev->physical_device->local_fd, DRM_IOCTL_GEM_CLOSE, &req);
123 }
124
125 /** Helper for DRM_MSM_GEM_INFO, returns 0 on error. */
126 static uint64_t
127 tu_gem_info(const struct tu_device *dev, uint32_t gem_handle, uint32_t info)
128 {
129 struct drm_msm_gem_info req = {
130 .handle = gem_handle,
131 .info = info,
132 };
133
134 int ret = drmCommandWriteRead(dev->physical_device->local_fd,
135 DRM_MSM_GEM_INFO, &req, sizeof(req));
136 if (ret < 0)
137 return 0;
138
139 return req.value;
140 }
141
142 static VkResult
143 tu_bo_init(struct tu_device *dev,
144 struct tu_bo *bo,
145 uint32_t gem_handle,
146 uint64_t size)
147 {
148 uint64_t iova = tu_gem_info(dev, gem_handle, MSM_INFO_GET_IOVA);
149 if (!iova) {
150 tu_gem_close(dev, gem_handle);
151 return VK_ERROR_OUT_OF_DEVICE_MEMORY;
152 }
153
154 *bo = (struct tu_bo) {
155 .gem_handle = gem_handle,
156 .size = size,
157 .iova = iova,
158 };
159
160 return VK_SUCCESS;
161 }
162
163 VkResult
164 tu_bo_init_new(struct tu_device *dev, struct tu_bo *bo, uint64_t size)
165 {
166 /* TODO: Choose better flags. As of 2018-11-12, freedreno/drm/msm_bo.c
167 * always sets `flags = MSM_BO_WC`, and we copy that behavior here.
168 */
169 struct drm_msm_gem_new req = {
170 .size = size,
171 .flags = MSM_BO_WC
172 };
173
174 int ret = drmCommandWriteRead(dev->physical_device->local_fd,
175 DRM_MSM_GEM_NEW, &req, sizeof(req));
176 if (ret)
177 return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
178
179 return tu_bo_init(dev, bo, req.handle, size);
180 }
181
182 VkResult
183 tu_bo_init_dmabuf(struct tu_device *dev,
184 struct tu_bo *bo,
185 uint64_t size,
186 int prime_fd)
187 {
188 /* lseek() to get the real size */
189 off_t real_size = lseek(prime_fd, 0, SEEK_END);
190 lseek(prime_fd, 0, SEEK_SET);
191 if (real_size < 0 || (uint64_t) real_size < size)
192 return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
193
194 uint32_t gem_handle;
195 int ret = drmPrimeFDToHandle(dev->physical_device->local_fd, prime_fd,
196 &gem_handle);
197 if (ret)
198 return vk_error(dev->instance, VK_ERROR_INVALID_EXTERNAL_HANDLE);
199
200 return tu_bo_init(dev, bo, gem_handle, size);
201 }
202
203 int
204 tu_bo_export_dmabuf(struct tu_device *dev, struct tu_bo *bo)
205 {
206 int prime_fd;
207 int ret = drmPrimeHandleToFD(dev->physical_device->local_fd, bo->gem_handle,
208 DRM_CLOEXEC, &prime_fd);
209
210 return ret == 0 ? prime_fd : -1;
211 }
212
213 VkResult
214 tu_bo_map(struct tu_device *dev, struct tu_bo *bo)
215 {
216 if (bo->map)
217 return VK_SUCCESS;
218
219 uint64_t offset = tu_gem_info(dev, bo->gem_handle, MSM_INFO_GET_OFFSET);
220 if (!offset)
221 return vk_error(dev->instance, VK_ERROR_OUT_OF_DEVICE_MEMORY);
222
223 /* TODO: Should we use the wrapper os_mmap() like Freedreno does? */
224 void *map = mmap(0, bo->size, PROT_READ | PROT_WRITE, MAP_SHARED,
225 dev->physical_device->local_fd, offset);
226 if (map == MAP_FAILED)
227 return vk_error(dev->instance, VK_ERROR_MEMORY_MAP_FAILED);
228
229 bo->map = map;
230 return VK_SUCCESS;
231 }
232
233 void
234 tu_bo_finish(struct tu_device *dev, struct tu_bo *bo)
235 {
236 assert(bo->gem_handle);
237
238 if (bo->map)
239 munmap(bo->map, bo->size);
240
241 tu_gem_close(dev, bo->gem_handle);
242 }
243
244 static VkResult
245 tu_drm_device_init(struct tu_physical_device *device,
246 struct tu_instance *instance,
247 drmDevicePtr drm_device)
248 {
249 const char *path = drm_device->nodes[DRM_NODE_RENDER];
250 VkResult result = VK_SUCCESS;
251 drmVersionPtr version;
252 int fd;
253 int master_fd = -1;
254
255 fd = open(path, O_RDWR | O_CLOEXEC);
256 if (fd < 0) {
257 return vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
258 "failed to open device %s", path);
259 }
260
261 /* Version 1.3 added MSM_INFO_IOVA. */
262 const int min_version_major = 1;
263 const int min_version_minor = 3;
264
265 version = drmGetVersion(fd);
266 if (!version) {
267 close(fd);
268 return vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
269 "failed to query kernel driver version for device %s",
270 path);
271 }
272
273 if (strcmp(version->name, "msm")) {
274 drmFreeVersion(version);
275 close(fd);
276 return vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
277 "device %s does not use the msm kernel driver", path);
278 }
279
280 if (version->version_major != min_version_major ||
281 version->version_minor < min_version_minor) {
282 result = vk_errorf(instance, VK_ERROR_INCOMPATIBLE_DRIVER,
283 "kernel driver for device %s has version %d.%d, "
284 "but Vulkan requires version >= %d.%d",
285 path, version->version_major, version->version_minor,
286 min_version_major, min_version_minor);
287 drmFreeVersion(version);
288 close(fd);
289 return result;
290 }
291
292 device->msm_major_version = version->version_major;
293 device->msm_minor_version = version->version_minor;
294
295 drmFreeVersion(version);
296
297 if (instance->debug_flags & TU_DEBUG_STARTUP)
298 tu_logi("Found compatible device '%s'.", path);
299
300 vk_object_base_init(NULL, &device->base, VK_OBJECT_TYPE_PHYSICAL_DEVICE);
301 device->instance = instance;
302 assert(strlen(path) < ARRAY_SIZE(device->path));
303 strncpy(device->path, path, ARRAY_SIZE(device->path));
304
305 if (instance->enabled_extensions.KHR_display) {
306 master_fd =
307 open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
308 if (master_fd >= 0) {
309 /* TODO: free master_fd is accel is not working? */
310 }
311 }
312
313 device->master_fd = master_fd;
314 device->local_fd = fd;
315
316 if (tu_drm_get_gpu_id(device, &device->gpu_id)) {
317 if (instance->debug_flags & TU_DEBUG_STARTUP)
318 tu_logi("Could not query the GPU ID");
319 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
320 "could not get GPU ID");
321 goto fail;
322 }
323
324 if (tu_drm_get_gmem_size(device, &device->gmem_size)) {
325 if (instance->debug_flags & TU_DEBUG_STARTUP)
326 tu_logi("Could not query the GMEM size");
327 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
328 "could not get GMEM size");
329 goto fail;
330 }
331
332 if (tu_drm_get_gmem_base(device, &device->gmem_base)) {
333 if (instance->debug_flags & TU_DEBUG_STARTUP)
334 tu_logi("Could not query the GMEM size");
335 result = vk_errorf(instance, VK_ERROR_INITIALIZATION_FAILED,
336 "could not get GMEM size");
337 goto fail;
338 }
339
340 return tu_physical_device_init(device, instance);
341
342 fail:
343 close(fd);
344 if (master_fd != -1)
345 close(master_fd);
346 return result;
347 }
348
349 VkResult
350 tu_enumerate_devices(struct tu_instance *instance)
351 {
352 /* TODO: Check for more devices ? */
353 drmDevicePtr devices[8];
354 VkResult result = VK_ERROR_INCOMPATIBLE_DRIVER;
355 int max_devices;
356
357 instance->physical_device_count = 0;
358
359 max_devices = drmGetDevices2(0, devices, ARRAY_SIZE(devices));
360
361 if (instance->debug_flags & TU_DEBUG_STARTUP) {
362 if (max_devices < 0)
363 tu_logi("drmGetDevices2 returned error: %s\n", strerror(max_devices));
364 else
365 tu_logi("Found %d drm nodes", max_devices);
366 }
367
368 if (max_devices < 1)
369 return vk_error(instance, VK_ERROR_INCOMPATIBLE_DRIVER);
370
371 for (unsigned i = 0; i < (unsigned) max_devices; i++) {
372 if (devices[i]->available_nodes & 1 << DRM_NODE_RENDER &&
373 devices[i]->bustype == DRM_BUS_PLATFORM) {
374
375 result = tu_drm_device_init(
376 instance->physical_devices + instance->physical_device_count,
377 instance, devices[i]);
378 if (result == VK_SUCCESS)
379 ++instance->physical_device_count;
380 else if (result != VK_ERROR_INCOMPATIBLE_DRIVER)
381 break;
382 }
383 }
384 drmFreeDevices(devices, max_devices);
385
386 return result;
387 }
388