Linux DRM - Core Initialization and Driver Registration
How Linux DRM comes up: drm_core_init registers major number 226, and a driver such as vkms creates its /dev/dri nodes through drm_dev_init and drm_dev_register in Linux v6.19.
October 27, 2026
Linux DRM - Core Initialization and Driver Registration
Reqruies
- Kernel: Based on Linux v6.19 source
- Prerequisite: Linux DRM Part 1: Architecture and Overview
Introduction
| Part | Topic |
|---|---|
| Part 1 | Architecture and Overview |
In Part 1, we examined the structure of DRM (
drm_driver → drm_device → drm_minor → drm_file) and device nodes. In this post, we will trace how this structure is created in code across two stages: the DRM core reserving major number 226 at boot time, and a driver (here, the virtual driver vkms) creating the device and registering /dev/dri/card0 and renderD128.Code Flow 1: DRM Core Initialization
DRM is initialized once when the kernel boots. This is where major number 226, which we saw earlier, gets registered.
drivers/gpu/drm/drm_drv.c - drm_core_init() (L1236-1272)c
Source drivers/gpu/drm/drm_drv.c L1236-L1272 (Linux v6.19)
drm_sysfs_init() creates the /sys/class/drm class, and register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops) creates the 226 drm line in /proc/devices. At this point, no device exists yet. DRM has merely reserved the entire major number 226 and registered a single "stub" file operations structure that intercepts every open().Code Flow 2: The Driver Registers the Device
Now it is time for the driver to step in. Let's look at vkms. First, here is the
drm_driver defined by the driver.drivers/gpu/drm/vkms/vkms_drv.c - vkms_driver (L92-102)c
Source drivers/gpu/drm/vkms/vkms_drv.c L92-L102 (Linux v6.19)
DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM and fops created via DEFINE_DRM_GEM_FOPS are all it takes. The function that registers this driver is vkms_create().drivers/gpu/drm/vkms/vkms_drv.c - vkms_create() (L159-220)c
Source drivers/gpu/drm/vkms/vkms_drv.c L159-L220 (Linux v6.19)
Driver Registration Flow (Based on vkms)MermaidsequenceDiagram participant D as Driver (vkms_create) participant C as DRM core participant S as sysfs · devtmpfs · udev D->>C: devm_drm_dev_alloc(&vkms_driver) C->>C: drm_dev_init()<br/>drm_minor_alloc(RENDER, PRIMARY)<br/>drm_gem_init() D->>C: drm_vblank_init() D->>C: vkms_modeset_init()<br/>(Register planes · CRTCs · connectors) D->>C: drm_dev_register() C->>C: drm_minor_register(RENDER)<br/>drm_minor_register(PRIMARY) C->>S: device_add(minor->kdev) S-->>S: /dev/dri/card0 (226:0)<br/>/dev/dri/renderD128 (226:128)
Step 1: Allocation and Initialization (drm_dev_init)
devm_drm_dev_alloc() invokes drm_dev_init() via internal devm_drm_dev_init(). Here, it creates minors based on driver_features and initializes GEM.drivers/gpu/drm/drm_drv.c - drm_dev_init() (L701-796)c
Source drivers/gpu/drm/drm_drv.c L701-L796 (Linux v6.19)
If
DRIVER_RENDER is set, it allocates a render minor, and it always allocates a primary minor. drm_minor_alloc(), which allocates a minor, picks a minor number (xa_alloc) and prepares the struct device (kdev) for sysfs.drivers/gpu/drm/drm_drv.c - drm_minor_alloc() (L142-172)c
Source drivers/gpu/drm/drm_drv.c L142-L172 (Linux v6.19)
Step 2: Registration (drm_dev_register)
Once the driver has prepared all KMS objects, it calls drm_dev_register(). From this point onward, userspace can open the device.
drivers/gpu/drm/drm_drv.c - drm_dev_register() (L1056-1126)c
Source drivers/gpu/drm/drm_drv.c L1056-L1126 (Linux v6.19)
drm_minor_register() registers the device with sysfs via device_add() and populates the minor number table (xa_store). Later, open() uses this table to look up the minor.drivers/gpu/drm/drm_drv.c - drm_minor_register() (L174-212)c
Source drivers/gpu/drm/drm_drv.c L174-L212 (Linux v6.19)
Code Locations
| Step | Function | Location (Linux v6.19) |
|---|---|---|
| Boot: DRM core initialization (Register major number 226) | drm_core_init() | drivers/gpu/drm/drm_drv.c |
| Boot: Stub open (Swap fops) | drm_stub_open() | drivers/gpu/drm/drm_drv.c |
| Driver: Device allocation and minor creation | drm_dev_init() | drivers/gpu/drm/drm_drv.c |
| Driver: Minor number and sysfs device | drm_minor_alloc() | drivers/gpu/drm/drm_drv.c |
| Driver: card%d / renderD%d naming | drm_sysfs_minor_alloc() | drivers/gpu/drm/drm_sysfs.c |
| Driver: Registration (Create /dev/dri nodes) | drm_dev_register() | drivers/gpu/drm/drm_drv.c |
| Driver: Register minor with sysfs | drm_minor_register() | drivers/gpu/drm/drm_drv.c |
Summary
Here is what we covered in this post:
- Core initialization:
drm_core_init()registers major number 226 and sets up stub fops to intercept allopen()calls. - Driver registration: Through
devm_drm_dev_alloc()→drm_dev_init()(minor number allocation) →drm_dev_register()→drm_minor_register()→device_add(),/dev/dri/card0andrenderD128are created.
Now that the nodes are available, it's time for userspace to make use of them. Next, we will explore how
open() and ioctl() are handled inside the kernel.