The Failure
FreeBSD’s stock graphics/libdrm path treats FreeBSD DRM devices like PCI
devices. That is good enough for a desktop GPU, but the PinePhone Pro’s two
DRM devices are FDT/platform devices:
hw.dri.0.busid: platform:rk_drm0
hw.dri.0.name: rockchip 0x7a
hw.dri.1.busid: platform:panfrost0
hw.dri.1.name: panfrost 0x79
Mesa and wlroots ask libdrm for the device list before selecting a renderer.
With an unpatched libdrm, drmGetDevices2() returns zero useful devices,
Mesa’s loader reports failed device information, and Sway either falls back
to the wrong path or fails before renderer creation. The visible symptom is
glxinfo -B reporting llvmpipe or Sway not reaching the Panfrost renderer
at all.
Kernel Side
Our DRM overlay creates the metadata that userland needs. drm_sysctl_init()
adds hw.dri.N.name, hw.dri.N.busid, and hw.dri.N.modesetting; both the
Rockchip display driver and Panfrost call it after registering their DRM
devices. The busid helper emits platform:<device> for non-PCI parents.
188
drm_add_busid_modesetting(struct drm_device *dev, struct sysctl_ctx_list *ctx,
189
struct sysctl_oid *top)
190
{
191
struct sysctl_oid *oid;
192
device_t bsddev;
193
int domain, bus, slot, func;
194
195
bsddev = dev->dev;
196
197
printf("drm_busid: bsddev=%s parent=%s\n", device_get_nameunit(bsddev), device_get_name(device_get_parent(bsddev)));
198
199
/*
200
* Check if this is a platform (FDT) device or PCI device.
201
* Platform devices use "platform:name" busid format.
202
*/
203
if (device_get_parent(bsddev) != NULL &&
204
strcmp(device_get_name(device_get_parent(bsddev)), "pci") != 0) {
205
/* Platform device - use driver name and unit */
206
snprintf(dev->busid_str, sizeof(dev->busid_str),
207
"platform:%s", device_get_nameunit(bsddev));
208
} else {
209
/* PCI device */
210
domain = pci_get_domain(bsddev);
211
bus = pci_get_bus(bsddev);
212
slot = pci_get_slot(bsddev);
213
func = pci_get_function(bsddev);
214
snprintf(dev->busid_str, sizeof(dev->busid_str),
215
"pci:%04x:%02x:%02x.%d", domain, bus, slot, func);
216
}
217
printf("drm_busid: result=%s\n", dev->busid_str);
218
oid = SYSCTL_ADD_STRING(ctx, SYSCTL_CHILDREN(top), OID_AUTO, "busid",
219
CTLFLAG_RD, dev->busid_str, 0, NULL);
220
if (oid == NULL)
221
return (-ENOMEM);
222
dev->modesetting = (dev->driver->driver_features & DRIVER_MODESET) != 0;
223
oid = SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(top), OID_AUTO,
224
"modesetting", CTLFLAG_RD, &dev->modesetting, 0, NULL);
225
if (oid == NULL)
That kernel-side bridge landed in b3c2366 DRM sysctl + libdrm platform device support , and Panfrost’s
attach path calls it in 1f895ec panfrost: call drm_sysctl_init so libdrm can enumerate the device .
Userland Patch
The libdrm port overlay lives in the repo at
ports/graphics/libdrm/files/patch-xf86drm.c. It has three jobs:
drmParseSubsystemTypemapshw.dri.N.busid = platform:*toDRM_BUS_PLATFORM.drmParseOFBusInfocopies the part afterplatform:into the platform device fullname.drmParseOFDeviceInforeadshw.dri.N.name, strips the trailing hex unit text, and exposes the driver name as a compatible string.
1
--- xf86drm.c.orig 2026-04-06 16:08:04.207080000 -0500
2
+++ xf86drm.c 2026-04-06 16:10:00.969430000 -0500
3
@@ -3636,8 +3636,35 @@
4
return DRM_BUS_VIRTIO;
5
}
6
return subsystem_type;
7
-#elif defined(__OpenBSD__) || defined(__DragonFly__) || defined(__FreeBSD__)
8
+#elif defined(__OpenBSD__) || defined(__DragonFly__)
9
return DRM_BUS_PCI;
10
+#elif defined(__FreeBSD__)
11
+ {
12
+ char dname[SPECNAMELEN];
13
+ char sysctl_name[64];
14
+ char sysctl_val[256];
15
+ size_t sysctl_len;
16
+ int id;
17
+
18
+ if (!devname_r(makedev(maj, min), S_IFCHR, dname, sizeof(dname)))
19
+ return DRM_BUS_PCI;
20
+
21
+ /* Extract card number from dri/cardN or dri/renderDN */
22
+ if (sscanf(dname, "dri/card%d", &id) != 1) {
23
+ if (sscanf(dname, "dri/renderD%d", &id) == 1)
24
+ id -= 128;
25
+ else
26
+ return DRM_BUS_PCI;
27
+ }
28
+
29
+ snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.busid", id);
30
+ sysctl_len = sizeof(sysctl_val);
31
+ if (sysctlbyname(sysctl_name, sysctl_val, &sysctl_len, NULL, 0) == 0) {
32
+ if (strncmp(sysctl_val, "platform:", 9) == 0)
33
+ return DRM_BUS_PLATFORM;
34
+ }
35
+ return DRM_BUS_PCI;
36
+ }
37
#else
38
#warning "Missing implementation of drmParseSubsystemType"
39
return -EINVAL;
40
@@ -4316,6 +4343,34 @@
41
free(name);
42
43
return 0;
44
+#elif defined(__FreeBSD__)
45
+ {
46
+ char dname[SPECNAMELEN];
47
+ char sysctl_name[64];
48
+ char sysctl_val[256];
49
+ size_t sysctl_len;
50
+ int id;
51
+
52
+ if (!devname_r(makedev(maj, min), S_IFCHR, dname, sizeof(dname)))
53
+ return -EINVAL;
54
+ if (sscanf(dname, "dri/card%d", &id) != 1) {
55
+ if (sscanf(dname, "dri/renderD%d", &id) == 1)
56
+ id -= 128;
57
+ else
58
+ return -EINVAL;
59
+ }
60
+ snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.busid", id);
61
+ sysctl_len = sizeof(sysctl_val);
62
+ if (sysctlbyname(sysctl_name, sysctl_val, &sysctl_len, NULL, 0) != 0)
63
+ return -EINVAL;
64
+ /* busid is "platform:NAME", extract NAME as fullname */
65
+ if (strncmp(sysctl_val, "platform:", 9) == 0)
66
+ strncpy(fullname, sysctl_val + 9, DRM_PLATFORM_DEVICE_NAME_LEN - 1);
67
+ else
68
+ strncpy(fullname, sysctl_val, DRM_PLATFORM_DEVICE_NAME_LEN - 1);
69
+ fullname[DRM_PLATFORM_DEVICE_NAME_LEN - 1] = '\0';
70
+ return 0;
71
+ }
72
#else
73
#warning "Missing implementation of drmParseOFBusInfo"
74
return -EINVAL;
75
@@ -4376,6 +4431,36 @@
76
77
free(*compatible);
78
return err;
79
+#elif defined(__FreeBSD__)
80
+ {
81
+ char dname[SPECNAMELEN];
82
+ char sysctl_name[64];
83
+ char sysctl_val[256];
84
+ size_t sysctl_len;
85
+ int id;
86
+
87
+ if (!devname_r(makedev(maj, min), S_IFCHR, dname, sizeof(dname)))
88
+ return -EINVAL;
89
+ if (sscanf(dname, "dri/card%d", &id) != 1) {
90
+ if (sscanf(dname, "dri/renderD%d", &id) == 1)
91
+ id -= 128;
92
+ else
93
+ return -EINVAL;
94
+ }
95
+ snprintf(sysctl_name, sizeof(sysctl_name), "hw.dri.%d.name", id);
96
+ sysctl_len = sizeof(sysctl_val);
97
+ if (sysctlbyname(sysctl_name, sysctl_val, &sysctl_len, NULL, 0) != 0)
98
+ return -EINVAL;
99
+ /* Use driver name as compatible string */
100
+ /* Strip trailing hex after space: "rockchip 0x64" -> "rockchip" */
101
+ char *sp = strchr(sysctl_val, ' ');
102
+ if (sp) *sp = '\0';
103
+ *compatible = calloc(2, sizeof(char*));
104
+ if (!*compatible) return -ENOMEM;
105
+ (*compatible)[0] = strdup(sysctl_val);
The patch was pinned as a ports overlay in 3c6af28 ports: pin libdrm patch for FreeBSD platform DRM enumeration . The
ports/graphics/libdrm/README.md file is the build and verification recipe.
Verification
The patched binary should contain all of these strings:
strings /usr/local/lib/libdrm.so.2 | grep -E 'hw\.dri|platform:'
# hw.dri.%d.busid
# platform:
# hw.dri.%d.name
# hw.dri.%d.modesetting
The live behavior check is:
glxinfo -B
# OpenGL renderer string: Mali-T860 (Panfrost)
If a package install replaces libdrm, reinstall the patched aarch64 package
and lock it again. That exact regression happened during the 2026-05-05 GPU
stress pass when mesa-demos and glmark2 pulled the stock package back in.
◐ partial This is still a local patch, not an upstreamable
final design. One known rough edge remains: render-node minor mapping uses
renderD128 -> hw.dri.0, which is enough for Sway’s current path but is not a
complete FreeBSD platform-device enumeration model.