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

PartTopic
Part 1Architecture 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
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
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
Driver Registration Flow (Based on vkms)
Mermaid
sequenceDiagram 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
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

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
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

Code Locations

StepFunctionLocation (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 creationdrm_dev_init()drivers/gpu/drm/drm_drv.c
Driver: Minor number and sysfs devicedrm_minor_alloc()drivers/gpu/drm/drm_drv.c
Driver: card%d / renderD%d namingdrm_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 sysfsdrm_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 all open() calls.
  • Driver registration: Through devm_drm_dev_alloc() → drm_dev_init() (minor number allocation) → drm_dev_register() → drm_minor_register() → device_add(), /dev/dri/card0 and renderD128 are 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.
Jooojub
System S/W engineer
Explore Tags
Series
    Recent Post
    © 2026. jooojub. All right reserved.