[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