diff --git a/src/nouveau/vulkan/meson.build b/src/nouveau/vulkan/meson.build index 0b02e53b00d..6646d6e1bf9 100644 --- a/src/nouveau/vulkan/meson.build +++ b/src/nouveau/vulkan/meson.build @@ -57,6 +57,9 @@ nvk_files = files( 'nvk_upload_queue.h', 'nvk_wsi.c', 'nvk_wsi.h', + 'nvkmd/nouveau/nvkmd_nouveau.h', + 'nvkmd/nouveau/nvkmd_nouveau_dev.c', + 'nvkmd/nouveau/nvkmd_nouveau_pdev.c', 'nvkmd/nvkmd.c', 'nvkmd/nvkmd.h', ) diff --git a/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau.h b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau.h new file mode 100644 index 00000000000..2b4a17b7c5a --- /dev/null +++ b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau.h @@ -0,0 +1,46 @@ +/* + * Copyright © 2024 Collabora Ltd. and Red Hat Inc. + * SPDX-License-Identifier: MIT + */ +#ifndef NVKMD_DRM_H +#define NVKMD_DRM_H 1 + +#include "nvkmd/nvkmd.h" +#include "vk_drm_syncobj.h" + +#include + +struct nouveau_ws_device; + +struct nvkmd_nouveau_pdev { + struct nvkmd_pdev base; + + /* Used for get_vram_used() */ + struct nouveau_ws_device *ws_dev; + + int primary_fd; + + struct vk_sync_type syncobj_sync_type; + const struct vk_sync_type *sync_types[2]; +}; + +NVKMD_DECL_SUBCLASS(pdev, nouveau); + +VkResult nvkmd_nouveau_try_create_pdev(struct _drmDevice *drm_device, + struct vk_object_base *log_obj, + enum nvk_debug debug_flags, + struct nvkmd_pdev **pdev_out); + +struct nvkmd_nouveau_dev { + struct nvkmd_dev base; + + struct nouveau_ws_device *ws_dev; +}; + +NVKMD_DECL_SUBCLASS(dev, nouveau); + +VkResult nvkmd_nouveau_create_dev(struct nvkmd_pdev *pdev, + struct vk_object_base *log_obj, + struct nvkmd_dev **dev_out); + +#endif /* NVKMD_DRM_H */ diff --git a/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_dev.c b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_dev.c new file mode 100644 index 00000000000..2d2822c32b3 --- /dev/null +++ b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_dev.c @@ -0,0 +1,76 @@ +/* + * Copyright © 2024 Collabora Ltd. and Red Hat Inc. + * SPDX-License-Identifier: MIT + */ + +#include "nvkmd_nouveau.h" + +#include "nouveau_device.h" +#include "vk_log.h" + +#include + +VkResult +nvkmd_nouveau_create_dev(struct nvkmd_pdev *_pdev, + struct vk_object_base *log_obj, + struct nvkmd_dev **dev_out) +{ + struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev); + + struct nvkmd_nouveau_dev *dev = CALLOC_STRUCT(nvkmd_nouveau_dev); + if (dev == NULL) + return vk_error(log_obj, VK_ERROR_OUT_OF_HOST_MEMORY); + + dev->base.ops = &nvkmd_nouveau_dev_ops; + + drmDevicePtr drm_device = NULL; + int ret = drmGetDeviceFromDevId(pdev->base.drm.render_dev, 0, &drm_device); + if (ret != 0) { + FREE(dev); + return vk_errorf(log_obj, VK_ERROR_INITIALIZATION_FAILED, + "Failed to get DRM device: %m"); + } + + dev->ws_dev = nouveau_ws_device_new(drm_device, pdev->ws_dev->debug_flags); + drmFreeDevice(&drm_device); + if (dev->ws_dev == NULL) { + FREE(dev); + return vk_errorf(log_obj, VK_ERROR_INITIALIZATION_FAILED, + "Failed to get DRM device: %m"); + } + + *dev_out = &dev->base; + + return VK_SUCCESS; +} + +static void +nvkmd_nouveau_dev_destroy(struct nvkmd_dev *_dev) +{ + struct nvkmd_nouveau_dev *dev = nvkmd_nouveau_dev(_dev); + + nouveau_ws_device_destroy(dev->ws_dev); + FREE(dev); +} + +static inline uint64_t +nvkmd_nouveau_dev_get_gpu_timestamp(struct nvkmd_dev *_dev) +{ + struct nvkmd_nouveau_dev *dev = nvkmd_nouveau_dev(_dev); + + return nouveau_ws_device_timestamp(dev->ws_dev); +} + +static int +nvkmd_nouveau_dev_get_drm_fd(struct nvkmd_dev *_dev) +{ + struct nvkmd_nouveau_dev *dev = nvkmd_nouveau_dev(_dev); + + return dev->ws_dev->fd; +} + +const struct nvkmd_dev_ops nvkmd_nouveau_dev_ops = { + .destroy = nvkmd_nouveau_dev_destroy, + .get_gpu_timestamp = nvkmd_nouveau_dev_get_gpu_timestamp, + .get_drm_fd = nvkmd_nouveau_dev_get_drm_fd, +}; diff --git a/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c new file mode 100644 index 00000000000..67be9f29c2e --- /dev/null +++ b/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c @@ -0,0 +1,176 @@ +/* + * Copyright © 2024 Collabora Ltd. and Red Hat Inc. + * SPDX-License-Identifier: MIT + */ + +#include "nvkmd_nouveau.h" + +#include "nouveau_device.h" +#include "vk_log.h" + +#include +#include +#include +#include + +static bool +drm_device_is_nouveau(const char *path) +{ + int fd = open(path, O_RDWR | O_CLOEXEC); + if (fd < 0) + return false; + + drmVersionPtr ver = drmGetVersion(fd); + if (!ver) { + close(fd); + return false; + } + + const bool is_nouveau = !strncmp("nouveau", ver->name, ver->name_len); + + drmFreeVersion(ver); + close(fd); + + return is_nouveau; +} + +VkResult +nvkmd_nouveau_try_create_pdev(struct _drmDevice *drm_device, + struct vk_object_base *log_obj, + enum nvk_debug debug_flags, + struct nvkmd_pdev **pdev_out) +{ + if (!(drm_device->available_nodes & (1 << DRM_NODE_RENDER))) + return VK_ERROR_INCOMPATIBLE_DRIVER; + + switch (drm_device->bustype) { + case DRM_BUS_PCI: + if (drm_device->deviceinfo.pci->vendor_id != NVIDIA_VENDOR_ID) + return VK_ERROR_INCOMPATIBLE_DRIVER; + break; + + case DRM_BUS_PLATFORM: { + const char *compat_prefix = "nvidia,"; + bool found = false; + for (int i = 0; drm_device->deviceinfo.platform->compatible[i] != NULL; i++) { + if (strncmp(drm_device->deviceinfo.platform->compatible[0], compat_prefix, strlen(compat_prefix)) == 0) { + found = true; + break; + } + } + if (!found) + return VK_ERROR_INCOMPATIBLE_DRIVER; + break; + } + + default: + return VK_ERROR_INCOMPATIBLE_DRIVER; + } + + if (!drm_device_is_nouveau(drm_device->nodes[DRM_NODE_RENDER])) + return VK_ERROR_INCOMPATIBLE_DRIVER; + + struct nouveau_ws_device *ws_dev = + nouveau_ws_device_new(drm_device, debug_flags); + if (!ws_dev) + return vk_error(log_obj, VK_ERROR_INCOMPATIBLE_DRIVER); + + if (!ws_dev->has_vm_bind) { + nouveau_ws_device_destroy(ws_dev); + return vk_errorf(log_obj, VK_ERROR_INCOMPATIBLE_DRIVER, + "NVK Requires a Linux kernel version 6.6 or later"); + } + + struct stat st; + if (stat(drm_device->nodes[DRM_NODE_RENDER], &st)) { + nouveau_ws_device_destroy(ws_dev); + return vk_errorf(log_obj, VK_ERROR_INITIALIZATION_FAILED, + "fstat() failed on %s: %m", + drm_device->nodes[DRM_NODE_RENDER]); + } + const dev_t render_dev = st.st_rdev; + + struct nvkmd_nouveau_pdev *pdev = CALLOC_STRUCT(nvkmd_nouveau_pdev); + if (pdev == NULL) { + nouveau_ws_device_destroy(ws_dev); + return vk_error(log_obj, VK_ERROR_OUT_OF_HOST_MEMORY); + } + + pdev->base.ops = &nvkmd_nouveau_pdev_ops; + pdev->base.dev_info = ws_dev->info; + pdev->base.kmd_info = (struct nvkmd_info) { + .has_get_vram_used = nouveau_ws_device_vram_used(ws_dev) != 0, + .has_alloc_tiled = nouveau_ws_device_has_tiled_bo(ws_dev), + .has_map_fixed = true, + .has_overmap = true, + }; + + pdev->base.drm.render_dev = render_dev; + + /* DRM primary is optional */ + if ((drm_device->available_nodes & (1 << DRM_NODE_PRIMARY)) && + !stat(drm_device->nodes[DRM_NODE_PRIMARY], &st)) + pdev->base.drm.primary_dev = st.st_rdev; + + pdev->primary_fd = -1; + pdev->ws_dev = ws_dev; + + pdev->syncobj_sync_type = vk_drm_syncobj_get_type(ws_dev->fd); + pdev->sync_types[0] = &pdev->syncobj_sync_type; + pdev->sync_types[1] = NULL; + pdev->base.sync_types = pdev->sync_types; + + *pdev_out = &pdev->base; + + return VK_SUCCESS; +} + +static void +nvkmd_nouveau_pdev_destroy(struct nvkmd_pdev *_pdev) +{ + struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev); + + nouveau_ws_device_destroy(pdev->ws_dev); + FREE(pdev); +} + +static uint64_t +nvkmd_nouveau_pdev_get_vram_used(struct nvkmd_pdev *_pdev) +{ + struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev); + + return nouveau_ws_device_vram_used(pdev->ws_dev); +} + +static int +nvkmd_nouveau_pdev_get_drm_primary_fd(struct nvkmd_pdev *_pdev) +{ + struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev); + + if (pdev->primary_fd >= 0) + return pdev->primary_fd; + + if (pdev->base.drm.primary_dev == 0) + return -1; + + drmDevicePtr drm_device = NULL; + int ret = drmGetDeviceFromDevId(pdev->base.drm.primary_dev, 0, &drm_device); + if (ret != 0) + return -1; + + int fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC); + drmFreeDevice(&drm_device); + + /* TODO: Test if the FD is usable? */ + + pdev->primary_fd = fd; + + return pdev->primary_fd; +} + +const struct nvkmd_pdev_ops nvkmd_nouveau_pdev_ops = { + .destroy = nvkmd_nouveau_pdev_destroy, + .get_vram_used = nvkmd_nouveau_pdev_get_vram_used, + .get_drm_primary_fd = nvkmd_nouveau_pdev_get_drm_primary_fd, + .create_dev = nvkmd_nouveau_create_dev, +}; diff --git a/src/nouveau/vulkan/nvkmd/nvkmd.c b/src/nouveau/vulkan/nvkmd/nvkmd.c index 72a15db87a4..09044eb43ce 100644 --- a/src/nouveau/vulkan/nvkmd/nvkmd.c +++ b/src/nouveau/vulkan/nvkmd/nvkmd.c @@ -4,6 +4,7 @@ */ #include "nvkmd.h" +#include "nouveau/nvkmd_nouveau.h" VkResult nvkmd_try_create_pdev_for_drm(struct _drmDevice *drm_device, @@ -11,5 +12,6 @@ nvkmd_try_create_pdev_for_drm(struct _drmDevice *drm_device, enum nvk_debug debug_flags, struct nvkmd_pdev **pdev_out) { - return VK_ERROR_INCOMPATIBLE_DRIVER; + return nvkmd_nouveau_try_create_pdev(drm_device, log_obj, + debug_flags, pdev_out); }