[RFC] Documentation: DRM framework documentation
Hans Verkuil
hverkuil at xs4all.nl
Thu Jun 7 02:13:30 PDT 2012
Hi Laurent!
I completely missed this when you posted this a week ago, but thank you for
doing this. One suggestion: cross-post the next version to linux-media as well:
I think this is useful for V4L2 as well.
Some comments below:
On Wed 30 May 2012 15:13:29 Laurent Pinchart wrote:
> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> ---
> Documentation/drm.txt | 1265 +++++++++++++++++++++++++++++++++++++++++++++++++
> 1 files changed, 1265 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/drm.txt
>
> Hi everybody,
>
> Here's the DRM kernel framework documentation I wrote while developing the
> Renesas SH Mobile DRM driver. It hopefully covers most of what's needed to
> write a simple DRM driver (although some areas are not documented, such as
> properties or the fbdev compatibility layer).
>
> I can convert the documentation to DocBook if needed and integrate it with the
> existing "documentation stub". In that case I'm thinking of splitting the
> DocBook documentation in two parts, userspace API documentation (that someone
> would have to fill, any volunteer ? ;-)) and kernel API documentation. Would
> that be fine ?
>
> Last but not least, now that documentation exists (albeit in an incomplete
> state), we need to make sure it won't get outdated too quickly. As nobody will
> volunteer to maintain it (feel free to prove me wrong though), I'd like to
> propose the same rule that we already follow in V4L: any patch that touches
> the API won't get merged if it doesn't update the documentation. Do you think
> this could work out ?
I strongly recommend that this policy is adopted. It is working out very well
in V4L2. Documentation can be a pain, but if you do it when you add new
functionality (and you still remember what it was you did :-) ), then it isn't
too bad.
> As usual, review will be appreciated.
>
> diff --git a/Documentation/drm.txt b/Documentation/drm.txt
> new file mode 100644
> index 0000000..4d8843d
> --- /dev/null
> +++ b/Documentation/drm.txt
> @@ -0,0 +1,1265 @@
> + Architecture of a DRM driver
> +i ----------------------------
> +
> +Written by Laurent Pinchart <laurent.pinchart at ideasonboard.com>
> +Last revised: May 30, 2012
> +
> +
> +1. Driver initialization
> +------------------------
<snip>
> +3. KMS initialization
> +---------------------
> +
> +Drivers must first initialize the mode configuration core by calling
> +drm_mode_config_init() on the DRM device. The function initializes the
> +drm_device::mode_config field and never fails. Once done, mode configuration
> +must be setup by
> +
> + - int min_width, min_height
> + - int max_width, max_height
> +
> + Minimum and maximum width and height of the frame buffers in pixel units.
> +
> + - struct drm_mode_config_funcs *funcs
> +
> + Basic mode setting functions. See the Mode Setting Operations section for
> + details.
> +
> +
> +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.
> +
> +- CRCTs (struct drm_crtc)
typo: CRCT -> CRTC
> +
> +"A CRTC is an abstraction representing a part of the chip that contains a
> +pointer to a scanout buffer.
A definition of a 'scanout buffer' would be useful here. Also: what does CRTC
stand for?
In general, I think it would be good to explain abbreviations (DRM, GEM, KMS,
etc.) That way the terminology is easier to understand.
> 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."
> +
> +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.
> +
> +- 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.
> +
> +Planes are optional. To create a plane, a KMS drivers allocates and zeroes an
> +instances of struct drm_plane (possible as part of a larger structure) and
> +registers it with a call to drm_plane_init(). The function takes a bitmask of
> +the CRTCs that can be associated with the plane, a pointer to the plane
> +functions and a list of format supported formats.
> +
> +- 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."
> +
> +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.
> +
> +- 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."
> +
> +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
> +
> + 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() which
> +a pointer to the connector functions and a connector type, and exposed through
> +sysfs with a call to drm_sysfs_connector_add().
> +
> +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.
> +
> +
> +4. KMS 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_sysfs_connector_remove()
> +before calling drm_connector_cleanup().
> +
> +Connectors state change detection must be cleanup up with a call to
> +drm_kms_helper_poll_fini().
> +
> +
> +5. Vertical Blanking
> +--------------------
> +
> +Vertical blanking plays a major role in graphics rendering. To achieve
> +tear-free display, users must synchronize page flips and/or rendering to
> +vertical blanking. The DRM API offers ioctls to perform page flips
> +synchronized to vertical blanking and wait for vertical blanking.
> +
> +The DRM core handles most of the vertical blanking management logic, which
> +involves filtering out spurious interrupts, keeping race-free blanking
> +counters, coping with counter wrap-around and resets and keeping use counts.
> +It relies on the driver to generate vertical blanking interrupts and
> +optionally provide a hardware vertical blanking counter. Drivers must
> +implement the following operations.
> +
> + - int (*enable_vblank) (struct drm_device *dev, int crtc)
> + - void (*disable_vblank) (struct drm_device *dev, int crtc)
> +
> + Enable or disable vertical blanking interrupts for the given CRTC.
> +
> + - u32 (*get_vblank_counter) (struct drm_device *dev, int crtc)
> +
> + Retrieve the value of the vertical blanking counter for the given CRTC. If
> + the hardware maintains a vertical blanking counter its value should be
> + returned. Otherwise drivers can use the drm_vblank_count() helper function
> + to handle this operation.
> +
> +Drivers must initialize the vertical blanking handling core with a call to
> +drm_vblank_init() in their .load() operation. The function will set the struct
> +drm_device vblank_disable_allowed field to 0. This will keep vertical blanking
> +interrupts enabled permanently until the first mode set operation, where
> +vblank_disable_allowed is set to 1. The reason behind this is not clear.
> +Drivers can set the field to 1 after calling drm_vblank_init() to make
> +vertical blanking interrupts dynamically managed from the beginning.
> +
> +Vertical blanking interrupts can be enabled by the DRM core or by drivers
> +themselves (for instance to handle page flipping operations). The DRM core
> +maintains a vertical blanking use count to ensure that the interrupts are not
> +disabled while a user still needs them. To increment the use count, drivers
> +call drm_vblank_get(). Upon return vertical blanking interrupts are guaranteed
> +to be enabled.
> +
> +To decrement the use count drivers call drm_vblank_put(). Only when the use
> +count drops to zero will the DRM core disable the vertical blanking
> +interrupts after a delay by scheduling a timer. The delay is accessible
> +through the vblankoffdelay module parameter or the drm_vblank_offdelay global
> +variable and expressed in milliseconds. Its default value is 5000 ms.
> +
> +When a vertical blanking interrupt occurs drivers only need to call the
> +drm_handle_vblank() function to account for the interrupt.
> +
> +Resources allocated by drm_vblank_init() must be freed with a call to
> +drm_vblank_cleanup() in the driver .unload() operation handler.
> +
> +
> +6. Memory Management
> +--------------------
> +
> +Modern Linux systems require large amount of graphics memory to store frame
> +buffers, textures, vertices and other graphics-related data. Given the very
> +dynamic nature of many of that data, managing graphics memory efficiently is
> +thus crucial for the graphics stack and plays a central role in the DRM
> +infrastructure.
> +
> +The DRM core includes two memory managers, namely Translation Table Maps (TTM)
> +and Graphics Execution Manager (GEM). TTM was the first DRM memory manager to
> +be developed and tried to be a one-size-fits-them all solution. It provides a
> +single userspace API to accomodate the need of all hardware. This resulted in
> +a large, complex piece of code that turned out to be hard to use for driver
> +development and.
> +
> +GEM started as an Intel-sponsored project in reaction to TTM's complexity. Its
> +design philosophy is completely different: instead of providing a solution to
> +every graphics memory-related problems, GEM identified common code between
> +drivers and created a support library to share it.
> +
> +This document describes the use of the GEM memory manager only.
> +
> +The GEM design approach has resulted in a memory manager that doesn't provide
> +full coverage of all (or even all common) use cass in its userspace or kernel
typo: cass -> cases
> +API. GEM exposes a set of standard memory-related operations to userspace and
> +a set of helper functions to drivers, and let drivers implement
> +hardware-specific operations with their own private API.
> +
> +The GEM userspace API is described in http://lwn.net/Articles/283798/. While
> +slightly outdated, the document provides a good overview of the GEM API
> +principles. Buffer allocation and read and write operations, described as part
> +of the common GEM API, are currently implemented using driver-specific ioctls.
> +
> +GEM is data-agnostic. It manages abstract buffer objects without knowing what
> +individual buffers contain. APIs that require knowledge of buffer contents or
> +purpose, such as buffer allocation or synchronization primitives, are thus
> +outside of the scope of GEM and must be implemented using driver-specific
> +ioctls.
> +
> +- GEM Initialization
> +
> + Drivers that use GEM must set the DRIVER_GEM bit in the struct drm_driver
> + driver_features field. The DRM core will then automatically initialize the
> + GEM core before calling the .load() operation.
> +
> +- GEM Objects Creation
> +
> + GEM splits creation of GEM objects and allocation of the memory that backs
> + them in two distinct operations.
> +
> + GEM objects are represented by an instance of struct drm_gem_object. Drivers
> + usually need to extend GEM objects with private information and thus create
> + a driver-specific GEM object structure type that embeds an instance of
> + struct drm_gem_object.
> +
> + To create a GEM object, a driver allocates memory for an instance of its
> + specific GEM object type and initializes the embedded struct drm_gem_object
> + with a call to drm_gem_object_init(). The function takes a pointer to the
> + DRM device, a pointer to the GEM object and the buffer object size in bytes.
> +
> + GEM automatically allocate anonymous pageable memory through shmfs when an
> + object is initialized. drm_gem_object_init() will create an shmfs file of
> + the requested size and store it into the struct drm_gem_object filp field.
> + The memory is used as either main storage for the object when the graphics
> + hardware uses system memory directly or as a backing store otherwise.
> +
> + Anonymous pageable memory allocation is not always desired, for instance
> + when the hardware requires physically contiguous system memory as is often
> + the case in embedded devices. Drivers can create GEM objects with no shmfs
> + backing (called private GEM objects) by initializing them with a call to
> + drm_gem_private_object_init() instead of drm_gem_object_init(). Storage for
> + private GEM objects must be managed by drivers.
> +
> + Drivers that do no need to extend GEM objects with private information can
> + call the drm_gem_object_alloc() function to allocate and initialize a struct
> + drm_gem_object instance. The GEM core will call the optional driver
> + .gem_init_object() operation after initializing the GEM object with
> + drm_gem_object_init().
> +
> + int (*gem_init_object) (struct drm_gem_object *obj)
> +
> + No alloc-and-init function exists for private GEM objects.
> +
> +- GEM Objects Lifetime
> +
> + All GEM objects are reference-counted by the GEM core. References can be
> + acquired and release by calling drm_gem_object_reference() and
> + drm_gem_object_unreference() respectively. The caller must hold the
> + drm_device struct_mutex lock. As a convenience, GEM provides the
> + drm_gem_object_reference_unlocked() and
> + drm_gem_object_unreference_unlocked() functions that can be called without
> + holding the lock.
> +
> + When the last reference to a GEM object is released the GEM core calls the
> + drm_driver .gem_free_object() operation. That operation is mandatory for
> + GEM-enabled drivers and must free the GEM object and all associated
> + resources.
> +
> + void (*gem_free_object) (struct drm_gem_object *obj)
> +
> + Drivers are responsible for freeing all GEM object resources, including the
> + resources created by the GEM core. If an mmap offset has been created for
> + the object (in which case drm_gem_object::map_list::map is not NULL) it must
> + be freed by a call to drm_gem_free_mmap_offset(). The shmfs backing store
> + must be released by calling drm_gem_object_release() (that function can
> + safely be called if no shmfs backing store has been created).
> +
> +- GEM Objects Naming
> +
> + Communication between userspace and the kernel refers to GEM objects using
> + local handles, global names or, more recently, file descriptors. All of
> + those are 32-bit integer values; the usual Linux kernel limits apply to the
> + file descriptors.
> +
> + GEM handles are local to a DRM file. Applications get a handle to a GEM
> + object through a driver-specific ioctl, and can use that handle to refer
> + to the GEM object in other standard or driver-specific ioctls. Closing a DRM
> + file handle frees all its GEM handles and dereferences the associated GEM
> + objects.
> +
> + To create a handle for a GEM object drivers call drm_gem_handle_create().
> + The function takes a pointer to the DRM file and the GEM object and returns
> + a locally unique handle. When the handle is no longer needed drivers delete
> + it with a call to drm_gem_handle_delete(). Finally the GEM object associated
> + with a handle can be retrieved by a call to drm_gem_object_lookup().
> +
> + GEM names are similar in purpose to handles but are not local to DRM files.
> + They can be passed between processes to reference a GEM object globally.
> + Names can't be used directly to refer to objects in the DRM API,
> + applications must convert handles to names and names to handles using the
> + DRM_IOCTL_GEM_FLINK and DRM_IOCTL_GEM_OPEN ioctls respectively. The
> + conversion is handled by the DRM core without any driver-specific support.
> +
> + Similar to global names, GEM file descriptors are also used to share GEM
> + objects across processes. They offer additional security: as file
> + descriptors must be explictly sent over UNIX domain sockets to be shared
> + between applications, they can't be guessed like the globally unique GEM
> + names.
> +
> + Drivers that support GEM file descriptors, also known as the DRM PRIME API,
> + must set the DRIVER_PRIME bit in the struct drm_driver driver_features field
> + and implement the .prime_handle_to_fd() and .prime_fd_to_handle()
> + operations.
> +
> + int (*prime_handle_to_fd)(struct drm_device *dev,
> + struct drm_file *file_priv, uint32_t handle,
> + uint32_t flags, int *prime_fd)
> + int (*prime_fd_to_handle)(struct drm_device *dev,
> + struct drm_file *file_priv, int prime_fd,
> + uint32_t *handle)
> +
> + Those two operations convert a GEM handle to a PRIME file descriptor and
> + vice versa. While the PRIME file descriptors can be specific to a device,
> + their true power come from making them shareable between multiple devices
> + using the cross-subsystem dma-buf buffer sharing framework. For that reason
> + drivers are advised to use the drm_gem_prime_handle_to_fd() and
> + drm_gem_prime_fd_to_handle() helper functions as their PRIME operations
> + handlers.
> +
> + The dma-buf PRIME helpers rely on the driver .gem_prime_export() and
> + .gem_prime_import() operations to create a dma-buf instance from a GEM
> + object (exporter role) and to create a GEM object from a dma-buf instance
> + (importer role). These two operations are mandatory when using dma-buf with
> + DRM PRIME.
> +
> +- GEM Objects Mapping
> +
> + Because mapping operations are fairly heavyweight GEM favours read/write-
> + like access to buffers, implemented through driver-specific ioctls, over
> + mapping buffers to userspace. However, when random access to the buffer is
> + needed (to perform software rendering for instance), direct access to the
> + object can be more efficient.
> +
> + The mmap system call can't be used directly to map GEM objects, as they
> + don't have their own file handle. Two alternative methods currently co-exist
> + to map GEM objects to userspace. The first method uses a driver-specific
> + ioctl to perform the mapping operation, calling do_mmap() under the hood.
> + This is often considered dubious, seems to be discouraged for new
> + GEM-enabled driver, and will thus not be described here.
typo: driver -> drivers
> +
> + The second method uses the mmap system call on the DRM file handle.
> +
> + void *mmap(void *addr, size_t length, int prot, int flags, int fd,
> + off_t offset)
> +
> + DRM identifies the GEM object to be mapped by a fake offset passed through
> + the mmap offset argument. Prior to being mapped, a GEM object must thus be
> + associated with a fake offset. To do so, drivers must call
> + drm_gem_create_mmap_offset() on the object. The function allocates a fake
> + offset range from a pool and stores the offset divided by PAGE_SIZE in
> + obj->map_list.hash.key. Care must be taken not to call
> + drm_gem_create_mmap_offset() if a fake offset has already been allocated for
> + the object. This can be tested by obj->map_list.map being non-NULL.
> +
> + Once allocated, the fake offset value (obj->map_list.hash.key << PAGE_SHIFT)
> + must be passed to the application in a driver-specific way and can then be
> + used as the mmap offset argument.
> +
> + The GEM core provides a helper method drm_gem_mmap() to handle object
> + mapping. The method can be set directly as the mmap file operation handler.
> + It will look up the GEM object based on the offset value and set the VMA
> + operations to the drm_driver gem_vm_ops field. Note that drm_gem_mmap()
> + doesn't map memory to userspace, but relies on the driver-provided fault
> + handler to map pages individually.
> +
> + To use drm_gem_mmap(), drivers must fill the struct drm_driver gem_vm_ops
> + field with a pointer to VM operations.
> +
> + struct vm_operations_struct *gem_vm_ops
> +
> + struct vm_operations_struct {
> + void (*open)(struct vm_area_struct * area);
> + void (*close)(struct vm_area_struct * area);
> + int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);
> + }
> +
> + The open and close operations must update the GEM object reference count.
> + Drivers can use the drm_gem_vm_open() and drm_gem_vm_close() helper
> + functions directly as open and close handlers.
> +
> + The fault operation handler is responsible for mapping individual pages to
> + userspace when a page fault occurs. Depending on the memory allocation
> + scheme, drivers can allocate pages at fault time, or can decide to allocate
> + memory for the GEM object at the time the object is created.
> +
> + Drivers that want to map the GEM object upfront instead of handling page
> + faults can implement their own mmap file operation handler.
> +
> +- Dumb GEM Objects
> +
> + The GEM API doesn't standardize GEM objects creation and leaves it to
> + driver-specific ioctls. While not an issue for full-fledged graphics stacks
> + that include device-specific userspace components (in libdrm for instance),
> + this limit makes DRM-based early boot graphics unnecessarily complex.
> +
> + Dumb GEM objects partly alleviate the problem by providing a standard API to
> + create dumb buffers suitable for scanout, which can then be used to create
> + KMS frame buffers.
> +
> + To support dumb GEM objects drivers must implement the .dumb_create(),
> + .dumb_destroy() and .dumb_map_offset() operations.
> +
> + int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev,
> + struct drm_mode_create_dumb *args)
> +
> + The .dumb_create() operation creates a GEM object suitable for scanout based
> + on the width, height and depth from the struct drm_mode_create_dumb
> + argument. It fills the argument's handle, pitch and size fields with a
> + handle for the newly created GEM object and its line pitch and size in
> + bytes.
> +
> + int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev,
> + uint32_t handle)
> +
> + The .dumb_destroy() operation destroys a dumb GEM object created by
> + .dumb_create().
> +
> + int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev,
> + uint32_t handle, uint64_t *offset)
> +
> + The .dumb_map_offset() operation associates an mmap fake offset with the GEM
> + object given by the handle and returns it. Drivers must use the
> + drm_gem_create_mmap_offset() function to associate the fake offset as
> + described in the GEM Objects Mapping section.
> +
> +
> +7. Mid-layer
> +------------
> +
> +The CRTC, encoder and connector functions provided by the drivers implement
> +the DRM API. They're called by the DRM core and ioctl handlers to handle
> +device state changes and configuration request. As implementing those
> +functions often requires logic not specific to drivers, mid-layer helper
> +functions are available to avoid duplicating boilerplate code.
> +
> +The DRM core contains one mid-layer implementation. The mid-layer provides
> +implementations of several CRTC, encoder and connector functions (called from
> +the top of the mid-layer) that pre-process requests and call lower-level
> +functions provided by the driver (at the bottom of the mid-layer). For
> +instance, the drm_crtc_helper_set_config() function can be used to fill the
> +struct drm_crtc_funcs set_config field. When called, it will split the
> +set_config operation in smaller, simpler operations and call the driver to
> +handle them.
> +
> +To use the mid-layer, drivers call drm_crtc_helper_add(),
> +drm_encoder_helper_add() and drm_connector_helper_add() functions to install
> +their mid-layer bottom operations handlers, and fill the drm_crtc_funcs,
> +drm_encoder_funcs and drm_connector_funcs structures with pointers to the
> +mid-layer top API functions. Installing the mid-layer bottom operation
> +handlers is best done right after registering the corresponding KMS object.
> +
> +The mid-layer is not split between CRTC, encoder and connector operations. To
> +use it, a driver must provide bottom functions for all of the three KMS
> +entities.
> +
> +
> +8. Mode Setting Operations
> +--------------------------
> +
> +- struct drm_framebuffer *(*fb_create)(struct drm_device *dev,
> + struct drm_file *file_priv,
> + struct drm_mode_fb_cmd2 *mode_cmd)
> +
> + Create a new frame buffer.
> +
> + Frame buffers are abstract memory objects that provide a source of pixels to
> + scanout to a CRTC. Applications explicitly request the creation of frame
> + buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque
> + handle that can be passed to the KMS CRTC control, plane configuration and
> + page flip functions.
> +
> + Frame buffers rely on the underneath memory manager for low-level memory
> + operations. When creating a frame buffer applications pass a memory handle
> + (or a list of memory handles for multi-planar formats) through the
> + drm_mode_fb_cmd2 argument. This document assumes that the driver uses GEM,
> + those handles thus reference GEM objects.
> +
> + Drivers must first validate the requested frame buffer parameters passed
> + through the mode_cmd argument. In particular this is where invalid sizes,
> + pixel formats or pitches can be caught.
> +
> + If the parameters are deemed valid, drivers then create, initialize and
> + return an instance of struct drm_framebuffer. If desired the instance can be
> + embedded in a larger driver-specific structure. The new instance is
> + initialized with a call to drm_framebuffer_init() which takes a pointer to
> + DRM frame buffer operations (struct drm_framebuffer_funcs). Frame buffer
> + operations are
> +
> + - int (*create_handle)(struct drm_framebuffer *fb,
> + struct drm_file *file_priv, unsigned int *handle)
> +
> + Create a handle to the frame buffer underlying memory object. If the frame
> + buffer uses a multi-plane format, the handle will reference the memory
> + object associated with the first plane.
> +
> + Drivers call drm_gem_handle_create() to create the handle.
> +
> + - void (*destroy)(struct drm_framebuffer *framebuffer)
> +
> + Destroy the frame buffer object and frees all associated resources.
> + Drivers must call drm_framebuffer_cleanup() to free resources allocated by
> + the DRM core for the frame buffer object, and must make sure to
> + unreference all memory objects associated with the frame buffer. Handles
> + created by the .create_handle() operation are released by the DRM core.
> +
> + - int (*dirty)(struct drm_framebuffer *framebuffer,
> + struct drm_file *file_priv, unsigned flags, unsigned color,
> + struct drm_clip_rect *clips, unsigned num_clips)
> +
> + This optional operation notifies the driver that a region of the frame
> + buffer has changed in response to a DRM_IOCTL_MODE_DIRTYFB ioctl call.
> +
> + After initializing the drm_framebuffer instance drivers must fill its width,
> + height, pitches, offsets, depth, bits_per_pixel and pixel_format fields from
> + the values passed through the drm_mode_fb_cmd2 argument. They should call
> + the drm_helper_mode_fill_fb_struct() helper function to do so.
> +
> +- void (*output_poll_changed)(struct drm_device *dev)
> +
> + This operation notifies the driver that the status of one or more connectors
> + has changed. Drivers that use the fbdev helper can just call the
> + drm_fb_helper_hotplug_event() function to handle this operation.
> +
> +
> +9. CRTC Operations
> +-------------------
> +
> +- 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 KMS cleanup section.
> +
> +- 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.
> +
> + (FIXME: How should set_config interact with DPMS? If the CRTC is suspended,
> + should it be resumed?)
> +
> + The mid-layer provides a drm_crtc_helper_set_config() helper function. The
> + helper will try to locate the best encoder for each connector by calling the
> + connector .best_encoder helper operation. That operation is mandatory and
> + must return a pointer to the best encoder for the connector. For devices
> + that map connectors to encoders 1:1, the function simply returns the pointer
> + to the associated encoder.
> +
> + After locating the appropriate encoders, the helper function will call the
> + mandatory mode_fixup encoder and CRTC helper operations.
> +
> + - bool (*mode_fixup)(struct drm_encoder *encoder,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> + - bool (*mode_fixup)(struct drm_crtc *crtc,
> + const struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +
> + (FIXME: Should the mode argument be const? The i915 driver modifies
> + mode->clock in intel_dp_mode_fixup().)
> +
> + Let encoders and CRTC adjust the requested mode or reject it completely.
> + Those operations return true if the mode is accepted (possibly after being
> + adjusted) or false if it is rejected.
> +
> + The mode_fixup operation should reject the mode if it can't reasonably use
> + it. The definition of "reasonable" is currently fuzzy in this context. One
> + possible behaviour would be to set the adjusted mode to the panel timings
> + when a fixed-mode panel is used with hardware capable of scaling. Anothe
> + behaviour would be to accept any input mode and adjust it to the closest
> + mode supported by the hardware (FIXME: This needs to be clarified).
> +
> + If the new configuration after mode adjustment is identical to the current
> + configuration the helper function will return without performing any other
> + operation.
> +
> + If the adjusted mode is identical to the current mode but changes to the
> + frame buffer need to be applied, the drm_crtc_helper_set_config() function
> + will call the CRTC .mode_set_base() helper operation.
> +
> + - int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,
> + struct drm_framebuffer *old_fb)
> +
> + Move the CRTC on the current frame buffer (stored in crtc->fb) to position
> + (x,y). Any of the frame buffer, x position or y position may have been
> + modified.
> +
> + This helper operation is optional. If not provided, the
> + drm_crtc_helper_set_config() function will fall back to the .mode_set()
> + helper operation.
> +
> + (FIXME: Why are x and y passed as arguments, as they can be accessed
> + through crtc->x and crtc->y?)
> +
> + If the adjusted mode differs from the current mode, or if the
> + .mode_set_base() helper operation is not provided, the helper function
> + performs a full mode set sequence by calling the following mandatory
> + CRTC and encoder operations in order.
> +
> + - void (*prepare)(struct drm_encoder *encoder)
> + - void (*prepare)(struct drm_crtc *crtc)
> +
> + Those operations are called after validating the requested mode. Drivers
> + use them to perform device-specific operations required before setting the
> + new mode.
> +
> + - int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode, int x, int y,
> + struct drm_framebuffer *old_fb)
> + - void (*mode_set)(struct drm_encoder *encoder,
> + struct drm_display_mode *mode,
> + struct drm_display_mode *adjusted_mode)
> +
> + Those operations set the new mode. Depending on the device requirements,
> + the mode can be stored internally by the driver and applied in the commit
> + operations, or programmed to the hardware here.
> +
> + The crtc::mode_set operation returns 0 on success or a negative error code
> + if an error occurs. The encoder::mode_set operation isn't allowed to fail.
> +
> + - void (*commit)(struct drm_crtc *crtc)
> + - void (*commit)(struct drm_encoder *encoder)
> +
> + Those operations are called after setting the new mode. Upon return the
> + device must use the new mode and be fully operational.
> +
> +- 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
> + targetting 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 is already pending, the .page_flip() operation must return
> + -EBUSY.
> +
> + (FIXME: Should DRM allow queueing multiple page flips?)
> +
> + 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 fill the event::event sequence, tv_sec and tv_usec fields
> + with the associated vertical blanking count and timestamp, add the event to
> + the drm_file list of events to be signaled, and wake up any waiting process.
> + This can be performed with
> +
> + struct timeval now;
> +
> + event->event.sequence = drm_vblank_count_and_time(..., &now);
> + event->event.tv_sec = now.tv_sec;
> + event->event.tv_usec = now.tv_usec;
> +
> + spin_lock_irqsave(&dev->event_lock, flags);
> + list_add_tail(&event->base.link, &event->base.file_priv->event_list);
> + wake_up_interruptible(&event->base.file_priv->event_wait);
> + spin_unlock_irqrestore(&dev->event_lock, flags);
> +
> + (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()).
> +
> +
> +10. 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, the src_w and src_h values can be ignored.
> +
> +- 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 KMS cleanup section.
> +
> +
> +11. Encoder Operations
> +----------------------
> +
> +- void (*destroy)(struct drm_encoder *encoder)
> +
> + Called to destroy the encoder when not needed anymore. See the KMS cleanup
> + section.
> +
> +
> +12. Connector Operations
> +------------------------
> +
> +Unless otherwise state, all operations are mandatory.
> +
> +- status - connection status (connected, disconnected, unknown)
> +
> + The connection status is updated through polling or hotplug events when
> + supported (see the polled field description). 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.
> +
> +- 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.
> +
> + The mid-layer offers a drm_helper_connector_dpms() helper function that
> + tracks power state of connectors. When using the helper function drivers
> + only need to provide .dpms helper operations for CRTCs and encoders to apply
> + the DPMS state to the device.
> +
> + The mid-layer doesn't track the power state of CRTCs and encoders. The .dpms
> + operations can thus be called with a mode identical to the currently active
> + mode.
> +
> +- enum drm_connector_status (*detect)(struct drm_connector *connector,
> + bool force)
> +
> + Check to see if anything is attached to the connector. @force is set to
> + false whilst polling, 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.
> +
> +- 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).
> +
> + The mid-layer provides a drm_helper_probe_single_connector_modes() helper
> + function. The helper updates the connection status for the connector and
> + then retrieves a list of modes by calling the connector .get_modes helper
> + operation.
> +
> + The .get_modes helper operation is mandatory. It must fill the connector's
> + probed_modes list by parsing EDID data with drm_add_edid_modes() or calling
> + drm_mode_probed_add() directly for every supported mode. The operation
> + returns the number of modes it has detected.
> +
> + When adding modes manually the driver creates each mode with a call to
> + drm_mode_create() and must fill the following fields.
> +
> + - type: Mode type bitmask, a combination of
> +
> + DRM_MODE_TYPE_BUILTIN - not used?
> + DRM_MODE_TYPE_CLOCK_C - not used?
> + DRM_MODE_TYPE_CRTC_C - not used?
> + DRM_MODE_TYPE_PREFERRED - The preferred mode for the connector
> + DRM_MODE_TYPE_DEFAULT - not used?
> + DRM_MODE_TYPE_USERDEF - not used?
> + DRM_MODE_TYPE_DRIVER - The mode has been created by the driver (as opposed
> + to user-created modes)
> +
> + Drivers must set the DRM_MODE_TYPE_DRIVER bit for all modes they create,
> + and set the DRM_MODE_TYPE_PREFERRED bit for the preferred mode.
> +
> + - clock: Pixel clock frequency in kHz unit
> +
> + - hdisplay, hsync_start, hsync_end, htotal: Horizontal timing information
> + - vdisplay, vsync_start, vsync_end, vtotal: Vertical timing information
> +
> + Active Front Sync Back
> + Region Porch Porch
> + <-----------------------><----------------><-------------><-------------->
> +
> + //////////////////////|
> + ////////////////////// |
> + ////////////////////// |.................. ................
> + _______________
> +
> + <----- [hv]display ----->
> + <------------- [hv]sync_start ------------>
> + <--------------------- [hv]sync_end --------------------->
> + <-------------------------------- [hv]total ----------------------------->
> +
> + - hskew, vscan: ?
> +
> + - flags: Mode flags, a combination of
> +
> + DRM_MODE_FLAG_PHSYNC - Horizontal sync is active high
> + DRM_MODE_FLAG_NHSYNC - Horizontal sync is active low
> + DRM_MODE_FLAG_PVSYNC - Vertical sync is active high
> + DRM_MODE_FLAG_NVSYNC - Vertical sync is active low
> + DRM_MODE_FLAG_INTERLACE - Mode is interlaced
> + DRM_MODE_FLAG_DBLSCAN - Mode uses doublescan
> + DRM_MODE_FLAG_CSYNC - Mode uses composite sync
> + DRM_MODE_FLAG_PCSYNC - Composite sync is active high
> + DRM_MODE_FLAG_NCSYNC - Composite sync is active low
> + DRM_MODE_FLAG_HSKEW - hskew provided (not used?)
> + DRM_MODE_FLAG_BCAST - not used?
> + DRM_MODE_FLAG_PIXMUX - not used?
> + DRM_MODE_FLAG_DBLCLK - not used?
> + DRM_MODE_FLAG_CLKDIV2 - ?
> +
> + Note that modes marked with the INTERLACE or DBLSCAN flags will be
> + filtered out by drm_helper_probe_single_connector_modes() if the
> + connector's interlace_allowed or doublescan_allowed field is set to 0.
> +
> + - name: Mode name
> +
> + The driver must call drm_mode_set_name() to fill the mode name from the
> + hdisplay, vdisplay and interlace flag after filling the corresponding
> + fields.
> +
> + The vrefresh value is computed by drm_helper_probe_single_connector_modes().
> +
> + When parsing EDID data, drm_add_edid_modes() fill the connector display_info
> + width_mm and height_mm fields. When creating modes manually the .get_modes
> + helper operation must set the display_info width_mm and height_mm fields if
> + they haven't been set already (for instance at initilization time when a
> + fixed-size panel is attached to the connector). The mode width_mm and
> + height_mm fields are only used internally during EDID parsing and should not
> + be set when creating modes manually.
> +
> + The helper function filters out modes larger than max_width and max_height
> + if specified. It then calls the connector .mode_valid helper operation for
> + each mode in the probed list to check whether the mode is valid for the
> + connector. The helper is mandatory and returns MODE_OK for supported modes
> + and one of the enum drm_mode_status values (MODE_*) for unsupported modes.
> + As unsupported modes will be immediately removed an implementation can
> + return MODE_BAD regardless of the exact reason why the mode is not valid.
> +
> + Note that the .mode_valid helper operation is only called for modes detected
> + by the device, and *not* for modes set by the user through the CRTC
> + .set_config operation.
> +
> +- void (*destroy)(struct drm_connector *connector)
> +
> + Destroy the connector when not needed anymore. See the KMS cleanup section.
> +
> +
> +13. TODO
> +--------
> +
> +- Document the struct_mutex catch-all lock
> +- Document connector properties
> +
> +- crtc and encoder dpms helper operations are only mandatory if the disable
> + operation isn't provided.
> +- crtc and connector .save and .restore operations are only used internally in
> + drivers, should they be removed from the core?
> +- encoder mid-layer .save and .restore operations are only used internally in
> + drivers, should they be removed from the core?
> +- encoder mid-layer .detect operation is only used internally in drivers,
> + should it be removed from the core?
> +
> +- KMS drivers must call drm_vblank_pre_modeset() and drm_vblank_post_modeset()
> + around mode setting. Should this be done in the DRM core?
> +- vblank_disable_allowed is set to 1 in the first drm_vblank_post_modeset()
> + call and never set back to 0. It seems to be safe to permanently set it to 1
> + in drm_vblank_init() for KMS driver, and it might be safe for UMS drivers as
> + well. This should be investigated.
>
Impressive work, you clearly have way too much time on your hands :-)
Regards,
Hans
More information about the dri-devel
mailing list