KMS Initialization and Cleanup

CRTCs (struct drm_crtc)
Planes (struct drm_plane)
Encoders (struct drm_encoder)
Connectors (struct drm_connector)
Cleanup
Output discovery and initialization example
KMS API Functions
KMS Data Structures
KMS Locking

A KMS device is abstracted and exposed as a set of planes, CRTCs, encoders and connectors. KMS drivers must thus create and initialize all those objects at load time after initializing mode setting.

CRTCs (struct drm_crtc)

A CRTC is an abstraction representing a part of the chip that contains a pointer to a scanout buffer. Therefore, the number of CRTCs available determines how many independent scanout buffers can be active at any given time. The CRTC structure contains several fields to support this: a pointer to some video memory (abstracted as a frame buffer object), a display mode, and an (x, y) offset into the video memory to support panning or configurations where one piece of video memory spans multiple CRTCs.

CRTC Initialization

A KMS device must create and register at least one struct drm_crtc instance. The instance is allocated and zeroed by the driver, possibly as part of a larger structure, and registered with a call to drm_crtc_init with a pointer to CRTC functions.

CRTC Operations

Set Configuration
int (*set_config)(struct drm_mode_set *set);

Apply a new CRTC configuration to the device. The configuration specifies a CRTC, a frame buffer to scan out from, a (x,y) position in the frame buffer, a display mode and an array of connectors to drive with the CRTC if possible.

If the frame buffer specified in the configuration is NULL, the driver must detach all encoders connected to the CRTC and all connectors attached to those encoders and disable them.

This operation is called with the mode config lock held.

Note

Note that the drm core has no notion of restoring the mode setting state after resume, since all resume handling is in the full responsibility of the driver. The common mode setting helper library though provides a helper which can be used for this: drm_helper_resume_force_mode.

Page Flipping
int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                   struct drm_pending_vblank_event *event);

Schedule a page flip to the given frame buffer for the CRTC. This operation is called with the mode config mutex held.

Page flipping is a synchronization mechanism that replaces the frame buffer being scanned out by the CRTC with a new frame buffer during vertical blanking, avoiding tearing. When an application requests a page flip the DRM core verifies that the new frame buffer is large enough to be scanned out by the CRTC in the currently configured mode and then calls the CRTC page_flip operation with a pointer to the new frame buffer.

The page_flip operation schedules a page flip. Once any pending rendering targeting the new frame buffer has completed, the CRTC will be reprogrammed to display that frame buffer after the next vertical refresh. The operation must return immediately without waiting for rendering or page flip to complete and must block any new rendering to the frame buffer until the page flip completes.

If a page flip can be successfully scheduled the driver must set the drm_crtc->fb field to the new framebuffer pointed to by fb. This is important so that the reference counting on framebuffers stays balanced.

If a page flip is already pending, the page_flip operation must return -EBUSY.

To synchronize page flip to vertical blanking the driver will likely need to enable vertical blanking interrupts. It should call drm_vblank_get for that purpose, and call drm_vblank_put after the page flip completes.

If the application has requested to be notified when page flip completes the page_flip operation will be called with a non-NULL event argument pointing to a drm_pending_vblank_event instance. Upon page flip completion the driver must call drm_send_vblank_event to fill in the event and send to wake up any waiting processes. This can be performed with

            spin_lock_irqsave(&dev->event_lock, flags);
            ...
            drm_send_vblank_event(dev, pipe, event);
            spin_unlock_irqrestore(&dev->event_lock, flags);
            

Note

FIXME: Could drivers that don't need to wait for rendering to complete just add the event to dev->vblank_event_list and let the DRM core handle everything, as for "normal" vertical blanking events?

While waiting for the page flip to complete, the event->base.link list head can be used freely by the driver to store the pending event in a driver-specific list.

If the file handle is closed before the event is signaled, drivers must take care to destroy the event in their preclose operation (and, if needed, call drm_vblank_put).

Miscellaneous
  • void (*set_property)(struct drm_crtc *crtc,
                         struct drm_property *property, uint64_t value);

    Set the value of the given CRTC property to value. See the section called “KMS Properties” for more information about properties.

  • void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
                            uint32_t start, uint32_t size);

    Apply a gamma table to the device. The operation is optional.

  • void (*destroy)(struct drm_crtc *crtc);

    Destroy the CRTC when not needed anymore. See the section called “KMS Initialization and Cleanup”.

Planes (struct drm_plane)

A plane represents an image source that can be blended with or overlayed on top of a CRTC during the scanout process. Planes are associated with a frame buffer to crop a portion of the image memory (source) and optionally scale it to a destination size. The result is then blended with or overlayed on top of a CRTC.

The DRM core recognizes three types of planes:

  • DRM_PLANE_TYPE_PRIMARY represents a "main" plane for a CRTC. Primary planes are the planes operated upon by CRTC modesetting and flipping operations described in the section called “CRTC Operations”.
  • DRM_PLANE_TYPE_CURSOR represents a "cursor" plane for a CRTC. Cursor planes are the planes operated upon by the DRM_IOCTL_MODE_CURSOR and DRM_IOCTL_MODE_CURSOR2 ioctls.
  • DRM_PLANE_TYPE_OVERLAY represents all non-primary, non-cursor planes. Some drivers refer to these types of planes as "sprites" internally.

For compatibility with legacy userspace, only overlay planes are made available to userspace by default. Userspace clients may set the DRM_CLIENT_CAP_UNIVERSAL_PLANES client capability bit to indicate that they wish to receive a universal plane list containing all plane types.

Plane Initialization

To create a plane, a KMS drivers allocates and zeroes an instances of struct drm_plane (possibly as part of a larger structure) and registers it with a call to drm_universal_plane_init. The function takes a bitmask of the CRTCs that can be associated with the plane, a pointer to the plane functions, a list of format supported formats, and the type of plane (primary, cursor, or overlay) being initialized.

Cursor and overlay planes are optional. All drivers should provide one primary plane per CRTC (although this requirement may change in the future); drivers that do not wish to provide special handling for primary planes may make use of the helper functions described in the section called “Plane Helper Reference” to create and register a primary plane with standard capabilities.

Plane Operations

  • int (*update_plane)(struct drm_plane *plane, struct drm_crtc *crtc,
                            struct drm_framebuffer *fb, int crtc_x, int crtc_y,
                            unsigned int crtc_w, unsigned int crtc_h,
                            uint32_t src_x, uint32_t src_y,
                            uint32_t src_w, uint32_t src_h);

    Enable and configure the plane to use the given CRTC and frame buffer.

    The source rectangle in frame buffer memory coordinates is given by the src_x, src_y, src_w and src_h parameters (as 16.16 fixed point values). Devices that don't support subpixel plane coordinates can ignore the fractional part.

    The destination rectangle in CRTC coordinates is given by the crtc_x, crtc_y, crtc_w and crtc_h parameters (as integer values). Devices scale the source rectangle to the destination rectangle. If scaling is not supported, and the source rectangle size doesn't match the destination rectangle size, the driver must return a -EINVAL error.

  • int (*disable_plane)(struct drm_plane *plane);

    Disable the plane. The DRM core calls this method in response to a DRM_IOCTL_MODE_SETPLANE ioctl call with the frame buffer ID set to 0. Disabled planes must not be processed by the CRTC.

  • void (*destroy)(struct drm_plane *plane);

    Destroy the plane when not needed anymore. See the section called “KMS Initialization and Cleanup”.

Encoders (struct drm_encoder)

An encoder takes pixel data from a CRTC and converts it to a format suitable for any attached connectors. On some devices, it may be possible to have a CRTC send data to more than one encoder. In that case, both encoders would receive data from the same scanout buffer, resulting in a "cloned" display configuration across the connectors attached to each encoder.

Encoder Initialization

As for CRTCs, a KMS driver must create, initialize and register at least one struct drm_encoder instance. The instance is allocated and zeroed by the driver, possibly as part of a larger structure.

Drivers must initialize the struct drm_encoder possible_crtcs and possible_clones fields before registering the encoder. Both fields are bitmasks of respectively the CRTCs that the encoder can be connected to, and sibling encoders candidate for cloning.

After being initialized, the encoder must be registered with a call to drm_encoder_init. The function takes a pointer to the encoder functions and an encoder type. Supported types are

  • DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A
  • DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort
  • DRM_MODE_ENCODER_LVDS for display panels
  • DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, Component, SCART)
  • DRM_MODE_ENCODER_VIRTUAL for virtual machine displays

Encoders must be attached to a CRTC to be used. DRM drivers leave encoders unattached at initialization time. Applications (or the fbdev compatibility layer when implemented) are responsible for attaching the encoders they want to use to a CRTC.

Encoder Operations

Connectors (struct drm_connector)

A connector is the final destination for pixel data on a device, and usually connects directly to an external display device like a monitor or laptop panel. A connector can only be attached to one encoder at a time. The connector is also the structure where information about the attached display is kept, so it contains fields for display data, EDID data, DPMS & connection status, and information about modes supported on the attached displays.

Connector Initialization

Finally a KMS driver must create, initialize, register and attach at least one struct drm_connector instance. The instance is created as other KMS objects and initialized by setting the following fields.

interlace_allowed

Whether the connector can handle interlaced modes.

doublescan_allowed

Whether the connector can handle doublescan.

display_info

Display information is filled from EDID information when a display is detected. For non hot-pluggable displays such as flat panels in embedded systems, the driver should initialize the display_info.width_mm and display_info.height_mm fields with the physical size of the display.

polled

Connector polling mode, a combination of

DRM_CONNECTOR_POLL_HPD

The connector generates hotplug events and doesn't need to be periodically polled. The CONNECT and DISCONNECT flags must not be set together with the HPD flag.

DRM_CONNECTOR_POLL_CONNECT

Periodically poll the connector for connection.

DRM_CONNECTOR_POLL_DISCONNECT

Periodically poll the connector for disconnection.

Set to 0 for connectors that don't support connection status discovery.

The connector is then registered with a call to drm_connector_init with a pointer to the connector functions and a connector type, and exposed through sysfs with a call to drm_connector_register.

Supported connector types are

  • DRM_MODE_CONNECTOR_VGA
  • DRM_MODE_CONNECTOR_DVII
  • DRM_MODE_CONNECTOR_DVID
  • DRM_MODE_CONNECTOR_DVIA
  • DRM_MODE_CONNECTOR_Composite
  • DRM_MODE_CONNECTOR_SVIDEO
  • DRM_MODE_CONNECTOR_LVDS
  • DRM_MODE_CONNECTOR_Component
  • DRM_MODE_CONNECTOR_9PinDIN
  • DRM_MODE_CONNECTOR_DisplayPort
  • DRM_MODE_CONNECTOR_HDMIA
  • DRM_MODE_CONNECTOR_HDMIB
  • DRM_MODE_CONNECTOR_TV
  • DRM_MODE_CONNECTOR_eDP
  • DRM_MODE_CONNECTOR_VIRTUAL

Connectors must be attached to an encoder to be used. For devices that map connectors to encoders 1:1, the connector should be attached at initialization time with a call to drm_mode_connector_attach_encoder. The driver must also set the drm_connector encoder field to point to the attached encoder.

Finally, drivers must initialize the connectors state change detection with a call to drm_kms_helper_poll_init. If at least one connector is pollable but can't generate hotplug interrupts (indicated by the DRM_CONNECTOR_POLL_CONNECT and DRM_CONNECTOR_POLL_DISCONNECT connector flags), a delayed work will automatically be queued to periodically poll for changes. Connectors that can generate hotplug interrupts must be marked with the DRM_CONNECTOR_POLL_HPD flag instead, and their interrupt handler must call drm_helper_hpd_irq_event. The function will queue a delayed work to check the state of all connectors, but no periodic polling will be done.

Connector Operations

Note

Unless otherwise state, all operations are mandatory.

DPMS
void (*dpms)(struct drm_connector *connector, int mode);

The DPMS operation sets the power state of a connector. The mode argument is one of

  • DRM_MODE_DPMS_ON

  • DRM_MODE_DPMS_STANDBY

  • DRM_MODE_DPMS_SUSPEND

  • DRM_MODE_DPMS_OFF

In all but DPMS_ON mode the encoder to which the connector is attached should put the display in low-power mode by driving its signals appropriately. If more than one connector is attached to the encoder care should be taken not to change the power state of other displays as a side effect. Low-power mode should be propagated to the encoders and CRTCs when all related connectors are put in low-power mode.

Modes
int (*fill_modes)(struct drm_connector *connector, uint32_t max_width,
                      uint32_t max_height);

Fill the mode list with all supported modes for the connector. If the max_width and max_height arguments are non-zero, the implementation must ignore all modes wider than max_width or higher than max_height.

The connector must also fill in this operation its display_info width_mm and height_mm fields with the connected display physical size in millimeters. The fields should be set to 0 if the value isn't known or is not applicable (for instance for projector devices).

Connection Status

The connection status is updated through polling or hotplug events when supported (see polled). The status value is reported to userspace through ioctls and must not be used inside the driver, as it only gets initialized by a call to drm_mode_getconnector from userspace.

enum drm_connector_status (*detect)(struct drm_connector *connector,
                                        bool force);

Check to see if anything is attached to the connector. The force parameter is set to false whilst polling or to true when checking the connector due to user request. force can be used by the driver to avoid expensive, destructive operations during automated probing.

Return connector_status_connected if something is connected to the connector, connector_status_disconnected if nothing is connected and connector_status_unknown if the connection state isn't known.

Drivers should only return connector_status_connected if the connection status has really been probed as connected. Connectors that can't detect the connection status, or failed connection status probes, should return connector_status_unknown.

Miscellaneous

Cleanup

The DRM core manages its objects' lifetime. When an object is not needed anymore the core calls its destroy function, which must clean up and free every resource allocated for the object. Every drm_*_init call must be matched with a corresponding drm_*_cleanup call to cleanup CRTCs (drm_crtc_cleanup), planes (drm_plane_cleanup), encoders (drm_encoder_cleanup) and connectors (drm_connector_cleanup). Furthermore, connectors that have been added to sysfs must be removed by a call to drm_connector_unregister before calling drm_connector_cleanup.

Connectors state change detection must be cleanup up with a call to drm_kms_helper_poll_fini.

Output discovery and initialization example

void intel_crt_init(struct drm_device *dev)
{
	struct drm_connector *connector;
	struct intel_output *intel_output;

	intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL);
	if (!intel_output)
		return;

	connector = &intel_output->base;
	drm_connector_init(dev, &intel_output->base,
			   &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);

	drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs,
			 DRM_MODE_ENCODER_DAC);

	drm_mode_connector_attach_encoder(&intel_output->base,
					  &intel_output->enc);

	/* Set up the DDC bus. */
	intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A");
	if (!intel_output->ddc_bus) {
		dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
			   "failed.\n");
		return;
	}

	intel_output->type = INTEL_OUTPUT_ANALOG;
	connector->interlace_allowed = 0;
	connector->doublescan_allowed = 0;

	drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
	drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);

	drm_connector_register(connector);
}

In the example above (taken from the i915 driver), a CRTC, connector and encoder combination is created. A device-specific i2c bus is also created for fetching EDID data and performing monitor detection. Once the process is complete, the new connector is registered with sysfs to make its properties available to applications.

KMS API Functions

drm_get_connector_status_name — return a string for connector status
drm_get_subpixel_order_name — return a string for a given subpixel enum
drm_get_format_name — return a string for drm fourcc format
drm_mode_object_find — look up a drm object with static lifetime
drm_framebuffer_init — initialize a framebuffer
drm_framebuffer_lookup — look up a drm framebuffer and grab a reference
drm_framebuffer_unreference — unref a framebuffer
drm_framebuffer_reference — incr the fb refcnt
drm_framebuffer_unregister_private — unregister a private fb from the lookup idr
drm_framebuffer_cleanup — remove a framebuffer object
drm_framebuffer_remove — remove and unreference a framebuffer object
drm_crtc_init_with_planes — Initialise a new CRTC object with specified primary and cursor planes.
drm_crtc_cleanup — Clean up the core crtc usage
drm_crtc_index — find the index of a registered CRTC
drm_display_info_set_bus_formats — set the supported bus formats
drm_connector_init — Init a preallocated connector
drm_connector_cleanup — cleans up an initialised connector
drm_connector_index — find the index of a registered connector
drm_connector_register — register a connector
drm_connector_unregister — unregister a connector
drm_connector_unplug_all — unregister connector userspace interfaces
drm_encoder_init — Init a preallocated encoder
drm_encoder_cleanup — cleans up an initialised encoder
drm_universal_plane_init — Initialize a new universal plane object
drm_plane_init — Initialize a legacy plane
drm_plane_cleanup — Clean up the core plane usage
drm_plane_index — find the index of a registered plane
drm_plane_force_disable — Forcibly disable a plane
drm_mode_create_dvi_i_properties — create DVI-I specific connector properties
drm_mode_create_tv_properties — create TV specific connector properties
drm_mode_create_scaling_mode_property — create scaling mode property
drm_mode_create_aspect_ratio_property — create aspect ratio property
drm_mode_create_dirty_info_property — create dirty property
drm_mode_create_suggested_offset_properties — create suggests offset properties
drm_mode_set_config_internal — helper to call ->set_config
drm_crtc_get_hv_timing — Fetches hdisplay/vdisplay for given mode
drm_crtc_check_viewport — Checks that a framebuffer is big enough for the CRTC viewport
drm_mode_legacy_fb_format — compute drm fourcc code from legacy description
drm_property_create — create a new property type
drm_property_create_enum — create a new enumeration property type
drm_property_create_bitmask — create a new bitmask property type
drm_property_create_range — create a new unsigned ranged property type
drm_property_create_signed_range — create a new signed ranged property type
drm_property_create_object — create a new object property type
drm_property_create_bool — create a new boolean property type
drm_property_add_enum — add a possible value to an enumeration property
drm_property_destroy — destroy a drm property
drm_object_attach_property — attach a property to a modeset object
drm_object_property_set_value — set the value of a property
drm_object_property_get_value — retrieve the value of a property
drm_mode_connector_set_path_property — set tile property on connector
drm_mode_connector_set_tile_property — set tile property on connector
drm_mode_connector_update_edid_property — update the edid property of a connector
drm_mode_plane_set_obj_prop — set the value of a property
drm_mode_connector_attach_encoder — attach a connector to an encoder
drm_mode_crtc_set_gamma_size — set the gamma table size
drm_mode_config_reset — call ->reset callbacks
drm_fb_get_bpp_depth — get the bpp/depth values for format
drm_format_num_planes — get the number of planes for format
drm_format_plane_cpp — determine the bytes per pixel value
drm_format_horz_chroma_subsampling — get the horizontal chroma subsampling factor
drm_format_vert_chroma_subsampling — get the vertical chroma subsampling factor
drm_rotation_simplify — Try to simplify the rotation
drm_mode_config_init — initialize DRM mode_configuration structure
drm_mode_config_cleanup — free up DRM mode_config info
drm_mode_get_tile_group — get a reference to an existing tile group
drm_mode_create_tile_group — create a tile group from a displayid description

KMS Data Structures

struct drm_crtc_state — mutable CRTC state
struct drm_crtc_funcs — control CRTCs for a given device
struct drm_crtc — central CRTC control structure
struct drm_connector_state — mutable connector state
struct drm_connector_funcs — control connectors on a given device
struct drm_encoder_funcs — encoder controls
struct drm_encoder — central DRM encoder structure
struct drm_connector — central DRM connector control structure
struct drm_plane_state — mutable plane state
struct drm_plane_funcs — driver plane control functions
struct drm_plane — central DRM plane control structure
struct drm_bridge_funcs — drm_bridge control functions
struct drm_bridge — central DRM bridge control structure
struct drm_atomic_state — the global state object for atomic updates
struct drm_mode_set — new values for a CRTC config change
struct drm_mode_config_funcs — basic driver provided mode setting functions
struct drm_mode_group — group of mode setting resources for potential sub-grouping
struct drm_mode_config — Mode configuration control structure
drm_for_each_plane_mask — iterate over planes specified by bitmask
drm_crtc_mask — find the mask of a registered CRTC
drm_encoder_crtc_ok — can a given crtc drive a given encoder?

KMS Locking

struct drm_modeset_acquire_ctx — locking context (see ww_acquire_ctx)
drm_modeset_lock_init — initialize lock
drm_modeset_lock_fini — cleanup lock
drm_modeset_is_locked — equivalent to mutex_is_locked
__drm_modeset_lock_all — internal helper to grab all modeset locks
drm_modeset_lock_all — take all modeset locks
drm_modeset_unlock_all — drop all modeset locks
drm_modeset_lock_crtc — lock crtc with hidden acquire ctx for a plane update
drm_modeset_legacy_acquire_ctx — find acquire ctx for legacy ioctls
drm_modeset_unlock_crtc — drop crtc lock
drm_warn_on_modeset_not_all_locked — check that all modeset locks are locked
drm_modeset_acquire_init — initialize acquire context
drm_modeset_acquire_fini — cleanup acquire context
drm_modeset_drop_locks — drop all locks
drm_modeset_backoff — deadlock avoidance backoff
drm_modeset_backoff_interruptible — deadlock avoidance backoff
drm_modeset_lock — take modeset lock
drm_modeset_lock_interruptible — take modeset lock
drm_modeset_unlock — drop modeset lock

As KMS moves toward more fine grained locking, and atomic ioctl where userspace can indirectly control locking order, it becomes necessary to use ww_mutex and acquire-contexts to avoid deadlocks. But because the locking is more distributed around the driver code, we want a bit of extra utility/tracking out of our acquire-ctx. This is provided by drm_modeset_lock / drm_modeset_acquire_ctx.

For basic principles of ww_mutex, see: Documentation/locking/ww-mutex-design.txt

The basic usage pattern is to:

drm_modeset_acquire_init(ctx) retry: foreach (lock in random_ordered_set_of_locks) { ret = drm_modeset_lock(lock, ctx) if (ret == -EDEADLK) { drm_modeset_backoff(ctx); goto retry; } }

... do stuff ...

drm_modeset_drop_locks(ctx); drm_modeset_acquire_fini(ctx);