xref: /aosp_15_r20/external/mesa3d/src/nouveau/vulkan/nvkmd/nouveau/nvkmd_nouveau_pdev.c (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1 /*
2  * Copyright © 2024 Collabora Ltd. and Red Hat Inc.
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "nvkmd_nouveau.h"
7 
8 #include "nouveau_device.h"
9 #include "util/os_misc.h"
10 #include "vk_log.h"
11 
12 #include <fcntl.h>
13 #include <string.h>
14 #include <sys/stat.h>
15 #include <xf86drm.h>
16 
17 static bool
drm_device_is_nouveau(const char * path)18 drm_device_is_nouveau(const char *path)
19 {
20    int fd = open(path, O_RDWR | O_CLOEXEC);
21    if (fd < 0)
22       return false;
23 
24    drmVersionPtr ver = drmGetVersion(fd);
25    if (!ver) {
26       close(fd);
27       return false;
28    }
29 
30    const bool is_nouveau = !strncmp("nouveau", ver->name, ver->name_len);
31 
32    drmFreeVersion(ver);
33    close(fd);
34 
35    return is_nouveau;
36 }
37 
38 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)39 nvkmd_nouveau_try_create_pdev(struct _drmDevice *drm_device,
40                               struct vk_object_base *log_obj,
41                               enum nvk_debug debug_flags,
42                               struct nvkmd_pdev **pdev_out)
43 {
44    if (!(drm_device->available_nodes & (1 << DRM_NODE_RENDER)))
45       return VK_ERROR_INCOMPATIBLE_DRIVER;
46 
47    switch (drm_device->bustype) {
48    case DRM_BUS_PCI:
49       if (drm_device->deviceinfo.pci->vendor_id != NVIDIA_VENDOR_ID)
50          return VK_ERROR_INCOMPATIBLE_DRIVER;
51       break;
52 
53    case DRM_BUS_PLATFORM: {
54       const char *compat_prefix = "nvidia,";
55       bool found = false;
56       for (int i = 0; drm_device->deviceinfo.platform->compatible[i] != NULL; i++) {
57          if (strncmp(drm_device->deviceinfo.platform->compatible[0], compat_prefix, strlen(compat_prefix)) == 0) {
58             found = true;
59             break;
60          }
61       }
62       if (!found)
63          return VK_ERROR_INCOMPATIBLE_DRIVER;
64       break;
65    }
66 
67    default:
68       return VK_ERROR_INCOMPATIBLE_DRIVER;
69    }
70 
71    if (!drm_device_is_nouveau(drm_device->nodes[DRM_NODE_RENDER]))
72       return VK_ERROR_INCOMPATIBLE_DRIVER;
73 
74    struct nouveau_ws_device *ws_dev = nouveau_ws_device_new(drm_device);
75    if (!ws_dev)
76       return vk_error(log_obj, VK_ERROR_INCOMPATIBLE_DRIVER);
77 
78    if (!ws_dev->has_vm_bind) {
79       nouveau_ws_device_destroy(ws_dev);
80       return vk_errorf(log_obj, VK_ERROR_INCOMPATIBLE_DRIVER,
81                        "NVK Requires a Linux kernel version 6.6 or later");
82    }
83 
84    struct stat st;
85    if (stat(drm_device->nodes[DRM_NODE_RENDER], &st)) {
86       nouveau_ws_device_destroy(ws_dev);
87       return vk_errorf(log_obj, VK_ERROR_INITIALIZATION_FAILED,
88                        "fstat() failed on %s: %m",
89                        drm_device->nodes[DRM_NODE_RENDER]);
90    }
91    const dev_t render_dev = st.st_rdev;
92 
93    struct nvkmd_nouveau_pdev *pdev = CALLOC_STRUCT(nvkmd_nouveau_pdev);
94    if (pdev == NULL) {
95       nouveau_ws_device_destroy(ws_dev);
96       return vk_error(log_obj, VK_ERROR_OUT_OF_HOST_MEMORY);
97    }
98 
99    pdev->base.ops = &nvkmd_nouveau_pdev_ops;
100    pdev->base.debug_flags = debug_flags;
101    pdev->base.dev_info = ws_dev->info;
102    pdev->base.kmd_info = (struct nvkmd_info) {
103       .has_dma_buf = true,
104       .has_get_vram_used = nouveau_ws_device_vram_used(ws_dev) != 0,
105       .has_alloc_tiled = nouveau_ws_device_has_tiled_bo(ws_dev),
106       .has_map_fixed = true,
107       .has_overmap = true,
108    };
109 
110    /* Nouveau uses the OS page size for all pages, regardless of whether they
111     * come from VRAM or system RAM.
112     */
113    uint64_t os_page_size;
114    os_get_page_size(&os_page_size);
115    assert(os_page_size <= UINT32_MAX);
116    pdev->base.bind_align_B = os_page_size;
117 
118    pdev->base.drm.render_dev = render_dev;
119 
120    /* DRM primary is optional */
121    if ((drm_device->available_nodes & (1 << DRM_NODE_PRIMARY)) &&
122        !stat(drm_device->nodes[DRM_NODE_PRIMARY], &st))
123       pdev->base.drm.primary_dev = st.st_rdev;
124 
125    pdev->primary_fd = -1;
126    pdev->ws_dev = ws_dev;
127 
128    pdev->syncobj_sync_type = vk_drm_syncobj_get_type(ws_dev->fd);
129    pdev->sync_types[0] = &pdev->syncobj_sync_type;
130    pdev->sync_types[1] = NULL;
131    pdev->base.sync_types = pdev->sync_types;
132 
133    *pdev_out = &pdev->base;
134 
135    return VK_SUCCESS;
136 }
137 
138 static void
nvkmd_nouveau_pdev_destroy(struct nvkmd_pdev * _pdev)139 nvkmd_nouveau_pdev_destroy(struct nvkmd_pdev *_pdev)
140 {
141    struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev);
142 
143    nouveau_ws_device_destroy(pdev->ws_dev);
144    FREE(pdev);
145 }
146 
147 static uint64_t
nvkmd_nouveau_pdev_get_vram_used(struct nvkmd_pdev * _pdev)148 nvkmd_nouveau_pdev_get_vram_used(struct nvkmd_pdev *_pdev)
149 {
150    struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev);
151 
152    return nouveau_ws_device_vram_used(pdev->ws_dev);
153 }
154 
155 static int
nvkmd_nouveau_pdev_get_drm_primary_fd(struct nvkmd_pdev * _pdev)156 nvkmd_nouveau_pdev_get_drm_primary_fd(struct nvkmd_pdev *_pdev)
157 {
158    struct nvkmd_nouveau_pdev *pdev = nvkmd_nouveau_pdev(_pdev);
159 
160    if (pdev->primary_fd >= 0)
161       return pdev->primary_fd;
162 
163    if (pdev->base.drm.primary_dev == 0)
164       return -1;
165 
166    drmDevicePtr drm_device = NULL;
167    int ret = drmGetDeviceFromDevId(pdev->base.drm.primary_dev, 0, &drm_device);
168    if (ret != 0)
169       return -1;
170 
171    int fd = open(drm_device->nodes[DRM_NODE_PRIMARY], O_RDWR | O_CLOEXEC);
172    drmFreeDevice(&drm_device);
173 
174    /* TODO: Test if the FD is usable? */
175 
176    pdev->primary_fd = fd;
177 
178    return pdev->primary_fd;
179 }
180 
181 const struct nvkmd_pdev_ops nvkmd_nouveau_pdev_ops = {
182    .destroy = nvkmd_nouveau_pdev_destroy,
183    .get_vram_used = nvkmd_nouveau_pdev_get_vram_used,
184    .get_drm_primary_fd = nvkmd_nouveau_pdev_get_drm_primary_fd,
185    .create_dev = nvkmd_nouveau_create_dev,
186 };
187