nvk/nvkmd: Implement dev and pdev for nouveau

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/30033>
This commit is contained in:
Faith Ekstrand 2024-07-02 13:47:13 -05:00 committed by Marge Bot
parent c8b36bbcc0
commit 4db1bd5846
5 changed files with 304 additions and 1 deletions

View file

@ -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',
)

View file

@ -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 <sys/types.h>
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 */

View file

@ -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 <xf86drm.h>
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,
};

View file

@ -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 <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <xf86drm.h>
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,
};

View file

@ -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);
}