Hey, thanks Laurent, this was quite needed!<div><br></div><div>I apologize in advance for the html mail.. but reviewing this on the flight home from connect and can't figure out how to do plain text email w/ google's offline mail client :-(<br>
<br>On Wednesday, May 30, 2012, Laurent Pinchart wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Signed-off-by: Laurent Pinchart <<a>laurent.pinchart@ideasonboard.com</a>><br>
---<br>
Documentation/drm.txt | 1265 +++++++++++++++++++++++++++++++++++++++++++++++++<br>
1 files changed, 1265 insertions(+), 0 deletions(-)<br>
create mode 100644 Documentation/drm.txt<br>
<br>
Hi everybody,<br>
<br>
Here's the DRM kernel framework documentation I wrote while developing the<br>
Renesas SH Mobile DRM driver. It hopefully covers most of what's needed to<br>
write a simple DRM driver (although some areas are not documented, such as<br>
properties or the fbdev compatibility layer).<br>
<br>
I can convert the documentation to DocBook if needed and integrate it with the<br>
existing "documentation stub". In that case I'm thinking of splitting the<br>
DocBook documentation in two parts, userspace API documentation (that someone<br>
would have to fill, any volunteer ? ;-)) and kernel API documentation. Would<br>
that be fine ?<br>
<br>
Last but not least, now that documentation exists (albeit in an incomplete<br>
state), we need to make sure it won't get outdated too quickly. As nobody will<br>
volunteer to maintain it (feel free to prove me wrong though), I'd like to<br>
propose the same rule that we already follow in V4L: any patch that touches<br>
the API won't get merged if it doesn't update the documentation. Do you think<br>
this could work out ?<br>
<br></blockquote><div><br></div><div>seems like a pretty reasonable idea</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
As usual, review will be appreciated.<br>
<br>
diff --git a/Documentation/drm.txt b/Documentation/drm.txt<br>
new file mode 100644<br>
index 0000000..4d8843d<br>
--- /dev/null<br>
+++ b/Documentation/drm.txt<br>
@@ -0,0 +1,1265 @@<br>
+ Architecture of a DRM driver<br>
+i ----------------------------<br>
+<br>
+Written by Laurent Pinchart <<a>laurent.pinchart@ideasonboard.com</a>><br>
+Last revised: May 30, 2012<br>
+<br>
+<br>
+1. Driver initialization<br>
+------------------------<br>
+<br>
+- Create a static struct drm_driver instance and register it at probe() time<br>
+ with drm_platform_init(). This will call the DRM driver load() method, if<br>
+ provided (why would the method not be provided?).<br>
+<br>
+ - int (*load) (struct drm_device *, unsigned long flags)<br>
+<br>
+ The method takes two arguments, a pointer to the newly created drm_device<br>
+ and flags. The flags are used to pass the driver_data field of the device id<br>
+ corresponding to the device passed to drm_*_init(). Only PCI devices<br>
+ currently use this, USB and platform DRM drivers have their load() method<br>
+ called with flags to 0.<br>
+<br>
+ The load method is responsible for performing resource allocation, hardware<br>
+ initialization and DRM initialization. See the IRQ registration and KMS<br>
+ initialization sections.<br>
+<br>
+ - int (*firstopen) (struct drm_device *)<br>
+ - void (*lastclose) (struct drm_device *)<br>
+ - int (*open) (struct drm_device *, struct drm_file *)<br>
+ - void (*preclose) (struct drm_device *, struct drm_file *)<br>
+ - void (*postclose) (struct drm_device *, struct drm_file *)<br>
+<br>
+ Open and close handlers. None of those methods are mandatory.<br>
+<br>
+ The .firstopen() method is called by the DRM core when an application opens<br>
+ a device that has no other opened file handle. Similarly the .lastclose()<br>
+ method is called when the last application holding a file handle opened on<br>
+ the device closes it. Both methods are mostly used for UMS (User Mode<br>
+ Setting) drivers to acquire and release device resources which should be<br>
+ done in the .load() and .unload() methods for KMS drivers.<br>
+<br>
+ Note that the .lastclose() method is also called at module unload time or,<br>
+ for hot-pluggable devices, when the device is unplugged. The .firstopen()<br>
+ and .lastclose() calls can thus be unbalanced.<br>
+<br></blockquote><div><br></div><div>AFAIK lastclose() should also be drm_fb_helper_restore_fbdev_mode() to restore fbcon mode. I'm also restoring crtc and plane properties to default values here, so a subsequent open of the device isn't inheriting some state from the previous user.</div>
<div><br></div><div>(maybe some of this could be handled instead in core, rather than each driver.. could be an idea for a cleanup?)</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ The .open() method is called every time the device is opened by an<br>
+ application. Drivers can allocate per-file private data in this method and<br>
+ store them in the struct drm_file::driver_priv field. Note that the .open()<br>
+ method is called before .firstopen().<br>
+<br>
+ The close operation is split into .preclose() and .postclose() methods.<br>
+ Drivers must stop and cleanup all per-file operations in the .preclose()<br>
+ method. For instance pending vertical blanking and page flip events must be<br>
+ cancelled. No per-file operation is allowed on the file handle after<br>
+ returning from the .preclose() method.<br></blockquote><div><br></div><div>oh, heh, I completely missed that in omapdrm, so looks like I need to do some cleanup around here.. I wonder if there is any sane way to make this simpler for the driver writer, since basically all drivers would need to do the same thing here.. Ie. keep track of pending page_flip events, sort of analogous to how the core keeps track of pending vblank events..</div>
<div><br></div><div>Are there any drivers that can queue up more than one page flip per crtc at a time? If no, we could track pending page flip event in 'struct drm_crtc' and then have a drm_crtc_handle_page_flip_events(crtc), similar to drm_handle_vblank_events()..</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ Finally the .postclose() method is called as the last step of the close<br>
+ operation, right before calling the .lastclose() method if no other open<br>
+ file handle exists for the device. Drivers that have allocated per-file<br>
+ private data in the .open() method should free it here.<br>
+<br>
+ - int (*suspend) (struct drm_device *, pm_message_t state)<br>
+ - int (*resume) (struct drm_device *)<br>
+<br>
+ Legacy suspend and resume methos. New driver should use the power management<br></blockquote><div><br></div><div>s/methos/methods/</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ interface provided by their bus type (usually through the struct<br>
+ device_driver dev_pm_ops) and set these methods to NULL.<br>
+<br>
+ - int (*enable_vblank) (struct drm_device *dev, int crtc)<br>
+ - void (*disable_vblank) (struct drm_device *dev, int crtc)<br>
+ - u32 (*get_vblank_counter) (struct drm_device *dev, int crtc)<br>
+<br>
+ Enable and disable vertical blanking interrupts and get the value of the<br>
+ vblank counter for the given CRTC. See the Vertical Blanking and Page<br>
+ Flipping section.<br>
+<br>
+ - int (*gem_init_object) (struct drm_gem_object *obj)<br>
+ - void (*gem_free_object) (struct drm_gem_object *obj)<br>
+<br>
+ GEM object initialization and free handlers. The initialization handler is<br>
+ only used in special cases and is optional. See the Memory Management<br>
+ section.<br>
+<br>
+ - int (*prime_handle_to_fd)(struct drm_device *dev,<br>
+ struct drm_file *file_priv, uint32_t handle,<br>
+ uint32_t flags, int *prime_fd)<br>
+ - int (*prime_fd_to_handle)(struct drm_device *dev,<br>
+ struct drm_file *file_priv, int prime_fd,<br>
+ uint32_t *handle)<br>
+ - struct dma_buf * (*gem_prime_export)(struct drm_device *dev,<br>
+ struct drm_gem_object *obj,<br>
+ int flags)<br>
+ - struct drm_gem_object * (*gem_prime_import)(struct drm_device *dev,<br>
+ struct dma_buf *dma_buf)<br>
+<br>
+ DRM PRIME file descriptor management. See the Memory Management section.<br>
+<br>
+ - int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev,<br>
+ struct drm_mode_create_dumb *args)<br>
+ - int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev,<br>
+ uint32_t handle, uint64_t *offset)<br>
+ - int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev,<br>
+ uint32_t handle)<br>
+<br>
+ Dumb GEM frame buffers management. See the Memory Management section.<br>
+<br>
+ - struct vm_operations_struct *gem_vm_ops<br>
+<br>
+ VMA operations for GEM objects. See the Memory Management section.<br>
+<br>
+ - major, minor, patchlevel<br>
+<br>
+ The driver major, minor and patch level versions, printed to the kernel log<br>
+ at initialization time and passed to userspace through DRM_IOCTL_VERSION.<br>
+<br>
+ The major and minor numbers are also used to verify the requested driver API<br>
+ version passed to DRM_IOCTL_SET_VERSION. When the driver API changes between<br>
+ minor versions, applications can call DRM_IOCTL_SET_VERSION to select a<br>
+ specific version of the API. If the requested major isn't equal to the<br>
+ driver major, or the requested minor is larger than the driver minor, the<br>
+ DRM_IOCTL_SET_VERSION call will return an error. Otherwise the driver's<br>
+ set_version() method is called with the requested version.<br>
+<br>
+ - name<br>
+<br>
+ The driver name, printed to the kernel log at initialization time, used for<br>
+ IRQ registration and passed to userspace through DRM_IOCTL_VERSION.<br>
+<br>
+ - desc<br>
+<br>
+ The driver description, passed to userspace through DRM_IOCTL_VERSION.<br>
+<br>
+ - date<br>
+<br>
+ The driver date as a string, printed to the kernel log at initialization time<br>
+ and passed to userspace through DRM_IOCTL_VERSION.<br>
+<br>
+ - features<br>
+<br>
+ Bitfield of driver capabilities and requirements, used by the DRM core to<br>
+ decide whether and how to implement parts of the DRM API.<br>
+<br>
+ DRIVER_USE_AGP - The DRM core will manage AGP resources<br>
+ DRIVER_REQUIRE_AGP - Make AGP initialization failure a fatal error<br>
+ DRIVER_USE_MTRR - The DRM core will manage MTRR resources<br>
+ DRIVER_PCI_DMA - Enable mapping of PCI DMA buffers to userspace<br>
+ DRIVER_SG - Enable SG buffers allocation and mapping<br>
+ DRIVER_HAVE_DMA - Enable userspace DMA API<br>
+ DRIVER_HAVE_IRQ - Make the DRM core register an interrupt handler<br>
+ DRIVER_IRQ_SHARED - Make the interrupt handler shared<br>
+ DRIVER_IRQ_VBL - Unused<br>
+ DRIVER_DMA_QUEUE - ???<br>
+ DRIVER_FB_DMA - Enable mapping of framebuffer DMA buffer to userspace<br>
+ DRIVER_IRQ_VBL2 - Unused<br>
+ DRIVER_GEM - Use the GEM memory manager<br>
+ DRIVER_MODESET - The driver implements the KMS API<br>
+ DRIVER_PRIME - The driver implements DRM PRIME buffer sharing<br>
+<br>
+ - struct drm_ioctl_desc *ioctls<br>
+ - int num_ioctls<br>
+<br>
+ Driver-specific ioctls descriptors table.<br>
+<br>
+ Driver-specific ioctls numbers start at DRM_COMMAND_BASE. The ioctls<br>
+ descriptors table is indexed by the ioctl number offset from the base value.<br>
+ Drivers can use the DRM_IOCTL_DEF_DRV() macro to initialize the table<br>
+ entries.<br>
+<br>
+ DRM_IOCTL_DEF_DRV(ioctl, func, flags)<br>
+<br>
+ - ioctl is the ioctl name. Drivers must define the DRM_##ioctl and<br>
+ DRM_IOCTL_##ioctl macros to the ioctl number offset from<br>
+ DRM_COMMAND_BASE and the ioctl number respectively. The first macro is<br>
+ private to the device while the second must be exposed to userspace in a<br>
+ public header.<br>
+<br>
+ - func is a pointer to the ioctl handler function compatible with the<br>
+ drm_ioctl_t type.<br>
+<br>
+ typedef int drm_ioctl_t(struct drm_device *dev, void *data,<br>
+ struct drm_file *file_priv);<br>
+<br>
+ - flags is a bitmask combination of the following values. It restricts how<br>
+ the ioctl is allowed to be called.<br>
+<br>
+ DRM_AUTH - Only authenticated callers allowed<br>
+ DRM_MASTER - The ioctl can only be called on the master file handle<br>
+ DRM_ROOT_ONLY - Only callers with the SYSADMIN capability allowed<br>
+ DRM_CONTROL_ALLOW - The ioctl can only be called on a control device<br>
+ DRM_UNLOCKED - The ioctl handler will be called without locking the DRM<br>
+ global mutex.<br>
+<br>
+ - const struct file_operations *fops<br>
+<br>
+ File operations for the DRM device node.<br>
+<br>
+ Drivers must define the file operations structure that forms the DRM<br>
+ userspace API entry point, even though most of those operations are<br>
+ implemented in the DRM core. The open, release and ioctl operations are<br>
+ handled by<br>
+<br>
+ .owner = THIS_MODULE,<br>
+ .open = drm_open,<br>
+ .release = drm_release,<br>
+ .unlocked_ioctl = drm_ioctl,<br>
+ #ifdef CONFIG_COMPAT<br>
+ .compat_ioctl = drm_compat_ioctl,<br>
+ #endif<br>
+<br>
+ Drivers that implement private ioctls that requires 32/64bit compatibility<br>
+ support must provide their own .compat_ioctl() handler that processes<br>
+ private ioctls and calls drm_compat_ioctl() for core ioctls.<br>
+<br>
+ The read and poll operations provide support for reading DRM events and<br>
+ polling them. They are implemented by<br>
+<br>
+ .poll = drm_poll,<br>
+ .read = drm_read,<br>
+ .fasync = drm_fasync,<br>
+ .llseek = no_llseek,<br>
+<br>
+ The memory mapping implementation varies depending on how the driver manages<br>
+ memory. Pre-GEM drivers will use drm_mmap(), while GEM-aware drivers will<br>
+ use drm_gem_mmap(). See the Memory Management section for more details.<br>
+<br>
+ .mmap = drm_gem_mmap,<br>
+<br>
+ No other file operation is supported by the DRM API.<br>
+<br>
+<br>
+2. IRQ registration<br>
+-------------------<br>
+<br>
+The DRM core tries to facilitate IRQ handler registration and unregistration<br>
+by providing drm_irq_install() and drm_irq_uninstall() methods. Those methods<br>
+only support a single interrupt per device.<br>
+<br>
+Both functions get the device IRQ by calling drm_dev_to_irq(). This inline<br>
+function will call a bus-specific operation to retrieve the IRQ number. For<br>
+platform devices, platform_get_irq(..., 0) is used to retrieve the IRQ number.<br>
+<br>
+drm_irq_install() starts by calling the irq_preinstall() driver operation. The<br>
+operation is optional and must make sure that the interrupt will not get fired<br>
+by clearing all pending interrupt flags or disabling the interrupt.<br>
+<br>
+The IRQ will then be requested by a call to request_irq(). If the<br>
+DRIVER_IRQ_SHARED driver feature flag is set, a shared (IRQF_SHARED) IRQ<br>
+handler will be requested.<br>
+<br>
+The IRQ handler function must be provided as the mandatory irq_handler driver<br>
+operation. It will get passed directly to request_irq() and thus has the same<br>
+prototype as all IRQ handlers. It will get called with a pointer to the DRM<br>
+device as the second argument.<br>
+<br>
+Finally the function calls the optional irq_postinstall() driver operation.<br>
+The operation usually enables interrupts (excluding the vblank interrupt,<br>
+which is enabled separately), but drivers may choose to enable/disable<br>
+interrupts at a different time.<br>
+<br>
+drm_irq_uninstall() is similarly used to uninstall an IRQ handler. It starts<br>
+by waking up all processes waiting on a vblank interrupt to make sure they<br>
+don't hang, and then calls the optional irq_uninstall() driver operation. The<br>
+operation must disable all hardware interrupts. Finally the function frees the<br>
+IRQ by calling free_irq().<br>
+<br>
+<br>
+3. KMS initialization<br>
+---------------------<br>
+<br>
+Drivers must first initialize the mode configuration core by calling<br>
+drm_mode_config_init() on the DRM device. The function initializes the<br>
+drm_device::mode_config field and never fails. Once done, mode configuration<br>
+must be setup by<br>
+<br>
+ - int min_width, min_height<br>
+ - int max_width, max_height<br>
+<br>
+ Minimum and maximum width and height of the frame buffers in pixel units.<br>
+<br>
+ - struct drm_mode_config_funcs *funcs<br>
+<br>
+ Basic mode setting functions. See the Mode Setting Operations section for<br>
+ details.<br>
+<br>
+<br>
+A KMS device is abstracted and exposed as a set of planes, CRTCs, encoders and<br>
+connectors. KMS drivers must thus create and initialize all those objects at<br>
+load time.<br>
+<br>
+- CRCTs (struct drm_crtc)<br>
+<br>
+"A CRTC is an abstraction representing a part of the chip that contains a<br>
+pointer to a scanout buffer. Therefore, the number of CRTCs available<br>
+determines how many independent scanout buffers can be active at any given<br>
+time. The CRTC structure contains several fields to support this: a pointer to<br>
+some video memory (abstracted as a frame buffer object), a display mode, and<br>
+an (x, y) offset into the video memory to support panning or configurations<br>
+where one piece of video memory spans multiple CRTCs."<br>
+<br>
+A KMS device must create and register at least one struct drm_crtc instance.<br>
+The instance is allocated and zeroed by the driver, possibly as part of a<br>
+larger structure, and registered with a call to drm_crtc_init() with a pointer<br>
+to CRTC functions.<br>
+<br>
+- Planes (struct drm_plane)<br>
+<br>
+A plane represents an image source that can be blended with or overlayed on<br>
+top of a CRTC during the scanout process. Planes are associated with a frame<br>
+buffer to crop a portion of the image memory (source) and optionally scale it<br>
+to a destination size. The result is then blended with or overlayed on top of<br>
+a CRTC.<br>
+<br>
+Planes are optional. To create a plane, a KMS drivers allocates and zeroes an<br>
+instances of struct drm_plane (possible as part of a larger structure) and<br>
+registers it with a call to drm_plane_init(). The function takes a bitmask of<br>
+the CRTCs that can be associated with the plane, a pointer to the plane<br>
+functions and a list of format supported formats.<br>
+<br>
+- Encoders (struct drm_encoder)<br>
+<br>
+"An encoder takes pixel data from a CRTC and converts it to a format suitable<br>
+for any attached connectors. On some devices, it may be possible to have a<br>
+CRTC send data to more than one encoder. In that case, both encoders would<br>
+receive data from the same scanout buffer, resulting in a "cloned" display<br>
+configuration across the connectors attached to each encoder."<br>
+<br>
+As for CRTCs, a KMS driver must create, initialize and register at least one<br>
+struct drm_encoder instance. The instance is allocated and zeroed by the<br>
+driver, possibly as part of a larger structure.<br>
+<br>
+Drivers must initialize the struct drm_encoder possible_crtcs and<br>
+possible_clones fields before registering the encoder. Both fields are<br>
+bitmasks of respectively the CRTCs that the encoder can be connected to, and<br>
+sibling encoders candidate for cloning.<br>
+<br>
+After being initialized, the encoder must be registered with a call to<br>
+drm_encoder_init(). The function takes a pointer to the encoder functions and<br>
+an encoder type. Supported types are<br>
+<br>
+ DRM_MODE_ENCODER_DAC for VGA and analog on DVI-I/DVI-A<br>
+ DRM_MODE_ENCODER_TMDS for DVI, HDMI and (embedded) DisplayPort<br>
+ DRM_MODE_ENCODER_LVDS for display panels<br>
+ DRM_MODE_ENCODER_TVDAC for TV output (Composite, S-Video, Component, SCART)<br>
+ DRM_MODE_ENCODER_VIRTUAL for virtual machine displays<br>
+<br>
+Encoders must be attached to a CRTC to be used. DRM drivers leave encoders<br>
+unattached at initialization time. Applications (or the fbdev compatibility<br>
+layer when implemented) are responsible for attaching the encoders they want<br>
+to use to a CRTC.<br>
+<br>
+- Connectors (struct drm_connector)<br>
+<br>
+"A connector is the final destination for pixel data on a device, and usually<br>
+connects directly to an external display device like a monitor or laptop<br>
+panel. A connector can only be attached to one encoder at a time. The<br>
+connector is also the structure where information about the attached display<br>
+is kept, so it contains fields for display data, EDID data, DPMS & connection<br>
+status, and information about modes supported on the attached displays."<br>
+<br>
+Finally a KMS driver must create, initialize, register and attach at least one<br>
+struct drm_connector instance. The instance is created as other KMS objects<br>
+and initialized by setting the following fields.<br>
+<br>
+ interlace_allowed - whether the connector can handle interlaced modes<br>
+ doublescan_allowed - whether the connector can handle doublescan<br>
+ display_info - display information<br>
+<br>
+ Display information is filled from EDID information when a display is<br>
+ detected. For non hot-pluggable displays such as flat panels in embedded<br>
+ systems, the driver should initialize the display_info.width_mm and<br>
+ display_info.height_mm fields with the physical size of the display.<br>
+<br>
+ polled - connector polling mode, a combination of<br>
+<br>
+ DRM_CONNECTOR_POLL_HPD<br>
+ The connector generates hotplug events and doesn't need to be<br>
+ periodically polled. The CONNECT and DISCONNECT flags must not be set<br>
+ together with the HPD flag.<br>
+ DRM_CONNECTOR_POLL_CONNECT<br>
+ Periodically poll the connector for connection.<br>
+ DRM_CONNECTOR_POLL_DISCONNECT<br>
+ Periodically poll the connector for disconnection.<br>
+<br>
+ Set to 0 for connectors that don't support connection status discovery.<br>
+<br>
+The connector is then registered with a call to drm_connector_init() which<br>
+a pointer to the connector functions and a connector type, and exposed through<br>
+sysfs with a call to drm_sysfs_connector_add().<br>
+<br>
+Supported connector types are<br>
+<br>
+ DRM_MODE_CONNECTOR_VGA<br>
+ DRM_MODE_CONNECTOR_DVII<br>
+ DRM_MODE_CONNECTOR_DVID<br>
+ DRM_MODE_CONNECTOR_DVIA<br>
+ DRM_MODE_CONNECTOR_Composite<br>
+ DRM_MODE_CONNECTOR_SVIDEO<br>
+ DRM_MODE_CONNECTOR_LVDS<br>
+ DRM_MODE_CONNECTOR_Component<br>
+ DRM_MODE_CONNECTOR_9PinDIN<br>
+ DRM_MODE_CONNECTOR_DisplayPort<br>
+ DRM_MODE_CONNECTOR_HDMIA<br>
+ DRM_MODE_CONNECTOR_HDMIB<br>
+ DRM_MODE_CONNECTOR_TV<br>
+ DRM_MODE_CONNECTOR_eDP<br>
+ DRM_MODE_CONNECTOR_VIRTUAL<br>
+<br>
+Connectors must be attached to an encoder to be used. For devices that map<br>
+connectors to encoders 1:1, the connector should be attached at initialization<br>
+time with a call to drm_mode_connector_attach_encoder(). The driver must also<br>
+set the drm_connector::encoder field to point to the attached encoder.<br>
+<br>
+<br>
+Finally, drivers must initialize the connectors state change detection with a<br>
+call to drm_kms_helper_poll_init(). If at least one connector is pollable but<br>
+can't generate hotplug interrupts (indicated by the DRM_CONNECTOR_POLL_CONNECT<br>
+and DRM_CONNECTOR_POLL_DISCONNECT connector flags), a delayed work will<br>
+automatically be queued to periodically poll for changes. Connectors that can<br>
+generate hotplug interrupts must be marked with the DRM_CONNECTOR_POLL_HPD<br>
+flag instead, and their interrupt handler must call<br>
+drm_helper_hpd_irq_event(). The function will queue a delayed work to check<br>
+the state of all connectors, but no periodic polling will be done.<br>
+<br>
+<br>
+4. KMS cleanup<br>
+--------------<br>
+<br>
+The DRM core manages its objects' lifetime. When an object is not needed<br>
+anymore the core calls its destroy function, which must clean up and free<br>
+every resource allocated for the object. Every drm_*_init() call must be<br>
+matched with a corresponding drm_*_cleanup() call to cleanup CRTCs<br>
+(drm_crtc_cleanup), planes (drm_plane_cleanup), encoders (drm_encoder_cleanup)<br>
+and connectors (drm_connector_cleanup). Furthermore, connectors that have been<br>
+added to sysfs must be removed by a call to drm_sysfs_connector_remove()<br>
+before calling drm_connector_cleanup().<br>
+<br>
+Connectors state change detection must be cleanup up with a call to<br>
+drm_kms_helper_poll_fini().<br>
+<br>
+<br>
+5. Vertical Blanking<br>
+--------------------<br>
+<br>
+Vertical blanking plays a major role in graphics rendering. To achieve<br>
+tear-free display, users must synchronize page flips and/or rendering to<br>
+vertical blanking. The DRM API offers ioctls to perform page flips<br>
+synchronized to vertical blanking and wait for vertical blanking.<br>
+<br>
+The DRM core handles most of the vertical blanking management logic, which<br>
+involves filtering out spurious interrupts, keeping race-free blanking<br>
+counters, coping with counter wrap-around and resets and keeping use counts.<br>
+It relies on the driver to generate vertical blanking interrupts and<br>
+optionally provide a hardware vertical blanking counter. Drivers must<br>
+implement the following operations.<br>
+<br>
+ - int (*enable_vblank) (struct drm_device *dev, int crtc)<br>
+ - void (*disable_vblank) (struct drm_device *dev, int crtc)<br>
+<br>
+ Enable or disable vertical blanking interrupts for the given CRTC.<br>
+<br>
+ - u32 (*get_vblank_counter) (struct drm_device *dev, int crtc)<br>
+<br>
+ Retrieve the value of the vertical blanking counter for the given CRTC. If<br>
+ the hardware maintains a vertical blanking counter its value should be<br>
+ returned. Otherwise drivers can use the drm_vblank_count() helper function<br>
+ to handle this operation.<br>
+<br>
+Drivers must initialize the vertical blanking handling core with a call to<br>
+drm_vblank_init() in their .load() operation. The function will set the struct<br>
+drm_device vblank_disable_allowed field to 0. This will keep vertical blanking<br>
+interrupts enabled permanently until the first mode set operation, where<br>
+vblank_disable_allowed is set to 1. The reason behind this is not clear.<br>
+Drivers can set the field to 1 after calling drm_vblank_init() to make<br>
+vertical blanking interrupts dynamically managed from the beginning.<br>
+<br>
+Vertical blanking interrupts can be enabled by the DRM core or by drivers<br>
+themselves (for instance to handle page flipping operations). The DRM core<br>
+maintains a vertical blanking use count to ensure that the interrupts are not<br>
+disabled while a user still needs them. To increment the use count, drivers<br>
+call drm_vblank_get(). Upon return vertical blanking interrupts are guaranteed<br>
+to be enabled.<br>
+<br>
+To decrement the use count drivers call drm_vblank_put(). Only when the use<br>
+count drops to zero will the DRM core disable the vertical blanking<br>
+interrupts after a delay by scheduling a timer. The delay is accessible<br>
+through the vblankoffdelay module parameter or the drm_vblank_offdelay global<br>
+variable and expressed in milliseconds. Its default value is 5000 ms.<br>
+<br>
+When a vertical blanking interrupt occurs drivers only need to call the<br>
+drm_handle_vblank() function to account for the interrupt.<br>
+<br>
+Resources allocated by drm_vblank_init() must be freed with a call to<br>
+drm_vblank_cleanup() in the driver .unload() operation handler.<br>
+<br>
+<br>
+6. Memory Management<br>
+--------------------<br>
+<br>
+Modern Linux systems require large amount of graphics memory to store frame<br>
+buffers, textures, vertices and other graphics-related data. Given the very<br>
+dynamic nature of many of that data, managing graphics memory efficiently is<br>
+thus crucial for the graphics stack and plays a central role in the DRM<br>
+infrastructure.<br>
+<br>
+The DRM core includes two memory managers, namely Translation Table Maps (TTM)<br>
+and Graphics Execution Manager (GEM). TTM was the first DRM memory manager to<br>
+be developed and tried to be a one-size-fits-them all solution. It provides a<br>
+single userspace API to accomodate the need of all hardware. This resulted in<br>
+a large, complex piece of code that turned out to be hard to use for driver<br>
+development and.<br></blockquote><div><br></div><div>not sure if that is a spurious 'and' or you somehow lost part of that sentence?</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+GEM started as an Intel-sponsored project in reaction to TTM's complexity. Its<br>
+design philosophy is completely different: instead of providing a solution to<br>
+every graphics memory-related problems, GEM identified common code between<br>
+drivers and created a support library to share it.<br>
+<br>
+This document describes the use of the GEM memory manager only.<br>
+<br>
+The GEM design approach has resulted in a memory manager that doesn't provide<br>
+full coverage of all (or even all common) use cass in its userspace or kernel<br>
+API. GEM exposes a set of standard memory-related operations to userspace and<br>
+a set of helper functions to drivers, and let drivers implement<br>
+hardware-specific operations with their own private API.<br>
+<br>
+The GEM userspace API is described in <a href="http://lwn.net/Articles/283798/" target="_blank">http://lwn.net/Articles/283798/</a>. While<br>
+slightly outdated, the document provides a good overview of the GEM API<br>
+principles. Buffer allocation and read and write operations, described as part<br>
+of the common GEM API, are currently implemented using driver-specific ioctls.<br>
+<br>
+GEM is data-agnostic. It manages abstract buffer objects without knowing what<br>
+individual buffers contain. APIs that require knowledge of buffer contents or<br>
+purpose, such as buffer allocation or synchronization primitives, are thus<br>
+outside of the scope of GEM and must be implemented using driver-specific<br>
+ioctls.<br>
+<br>
+- GEM Initialization<br>
+<br>
+ Drivers that use GEM must set the DRIVER_GEM bit in the struct drm_driver<br>
+ driver_features field. The DRM core will then automatically initialize the<br>
+ GEM core before calling the .load() operation.<br>
+<br>
+- GEM Objects Creation<br>
+<br>
+ GEM splits creation of GEM objects and allocation of the memory that backs<br>
+ them in two distinct operations.<br>
+<br>
+ GEM objects are represented by an instance of struct drm_gem_object. Drivers<br>
+ usually need to extend GEM objects with private information and thus create<br>
+ a driver-specific GEM object structure type that embeds an instance of<br>
+ struct drm_gem_object.<br>
+<br>
+ To create a GEM object, a driver allocates memory for an instance of its<br>
+ specific GEM object type and initializes the embedded struct drm_gem_object<br>
+ with a call to drm_gem_object_init(). The function takes a pointer to the<br>
+ DRM device, a pointer to the GEM object and the buffer object size in bytes.<br>
+<br>
+ GEM automatically allocate anonymous pageable memory through shmfs when an<br></blockquote><div><br></div><div><br></div><div>s/allocate/allocates/</div><div><br></div><div>Also, maybe worth mentioning that actual 'struct page's are allocated when drm_gem_get_pages() is called.. </div>
<div><br></div><div>(I suppose you could say that only the storage but not the memory is allocated when the object is created, but maybe that confuses things more.)</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ object is initialized. drm_gem_object_init() will create an shmfs file of<br>
+ the requested size and store it into the struct drm_gem_object filp field.<br>
+ The memory is used as either main storage for the object when the graphics<br>
+ hardware uses system memory directly or as a backing store otherwise.<br>
+<br>
+ Anonymous pageable memory allocation is not always desired, for instance<br>
+ when the hardware requires physically contiguous system memory as is often<br>
+ the case in embedded devices. Drivers can create GEM objects with no shmfs<br>
+ backing (called private GEM objects) by initializing them with a call to<br>
+ drm_gem_private_object_init() instead of drm_gem_object_init(). Storage for<br>
+ private GEM objects must be managed by drivers.<br>
+<br>
+ Drivers that do no need to extend GEM objects with private information can<br></blockquote><div><br></div><div>s/no/not/</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ call the drm_gem_object_alloc() function to allocate and initialize a struct<br>
+ drm_gem_object instance. The GEM core will call the optional driver<br>
+ .gem_init_object() operation after initializing the GEM object with<br>
+ drm_gem_object_init().<br>
+<br>
+ int (*gem_init_object) (struct drm_gem_object *obj)<br>
+<br>
+ No alloc-and-init function exists for private GEM objects.<br>
+<br>
+- GEM Objects Lifetime<br>
+<br>
+ All GEM objects are reference-counted by the GEM core. References can be<br>
+ acquired and release by calling drm_gem_object_reference() and<br>
+ drm_gem_object_unreference() respectively. The caller must hold the<br>
+ drm_device struct_mutex lock. As a convenience, GEM provides the<br>
+ drm_gem_object_reference_unlocked() and<br>
+ drm_gem_object_unreference_unlocked() functions that can be called without<br>
+ holding the lock.<br>
+<br>
+ When the last reference to a GEM object is released the GEM core calls the<br>
+ drm_driver .gem_free_object() operation. That operation is mandatory for<br>
+ GEM-enabled drivers and must free the GEM object and all associated<br>
+ resources.<br>
+<br>
+ void (*gem_free_object) (struct drm_gem_object *obj)<br>
+<br>
+ Drivers are responsible for freeing all GEM object resources, including the<br>
+ resources created by the GEM core. If an mmap offset has been created for<br>
+ the object (in which case drm_gem_object::map_list::map is not NULL) it must<br>
+ be freed by a call to drm_gem_free_mmap_offset(). The shmfs backing store<br>
+ must be released by calling drm_gem_object_release() (that function can<br>
+ safely be called if no shmfs backing store has been created).<br>
+<br>
+- GEM Objects Naming<br>
+<br>
+ Communication between userspace and the kernel refers to GEM objects using<br>
+ local handles, global names or, more recently, file descriptors. All of<br>
+ those are 32-bit integer values; the usual Linux kernel limits apply to the<br>
+ file descriptors.<br>
+<br>
+ GEM handles are local to a DRM file. Applications get a handle to a GEM<br>
+ object through a driver-specific ioctl, and can use that handle to refer<br>
+ to the GEM object in other standard or driver-specific ioctls. Closing a DRM<br>
+ file handle frees all its GEM handles and dereferences the associated GEM<br>
+ objects.<br>
+<br>
+ To create a handle for a GEM object drivers call drm_gem_handle_create().<br>
+ The function takes a pointer to the DRM file and the GEM object and returns<br>
+ a locally unique handle. When the handle is no longer needed drivers delete<br>
+ it with a call to drm_gem_handle_delete(). Finally the GEM object associated<br>
+ with a handle can be retrieved by a call to drm_gem_object_lookup().<br></blockquote><div><br></div><div><br></div><div>I suppose it is easy enough seen from the code, but might be worth mentioning that drm_gem_handle_create() increment's the refcnt of the gem obj, rather than taking ownership of the gem obj.. so in some cases the driver writer would want to drop that reference to the obj</div>
<div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ GEM names are similar in purpose to handles but are not local to DRM files.<br>
+ They can be passed between processes to reference a GEM object globally.<br>
+ Names can't be used directly to refer to objects in the DRM API,<br>
+ applications must convert handles to names and names to handles using the<br>
+ DRM_IOCTL_GEM_FLINK and DRM_IOCTL_GEM_OPEN ioctls respectively. The<br>
+ conversion is handled by the DRM core without any driver-specific support.<br>
+<br>
+ Similar to global names, GEM file descriptors are also used to share GEM<br>
+ objects across processes. They offer additional security: as file<br>
+ descriptors must be explictly sent over UNIX domain sockets to be shared<br>
+ between applications, they can't be guessed like the globally unique GEM<br>
+ names.<br>
+</blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ Drivers that support GEM file descriptors, also known as the DRM PRIME API,<br>
+ must set the DRIVER_PRIME bit in the struct drm_driver driver_features field<br>
+ and implement the .prime_handle_to_fd() and .prime_fd_to_handle()<br>
+ operations.<br>
+<br>
+ int (*prime_handle_to_fd)(struct drm_device *dev,<br>
+ struct drm_file *file_priv, uint32_t handle,<br>
+ uint32_t flags, int *prime_fd)<br>
+ int (*prime_fd_to_handle)(struct drm_device *dev,<br>
+ struct drm_file *file_priv, int prime_fd,<br>
+ uint32_t *handle)<br>
+<br>
+ Those two operations convert a GEM handle to a PRIME file descriptor and<br>
+ vice versa. While the PRIME file descriptors can be specific to a device,<br>
+ their true power come from making them shareable between multiple devices<br>
+ using the cross-subsystem dma-buf buffer sharing framework. For that reason<br>
+ drivers are advised to use the drm_gem_prime_handle_to_fd() and<br>
+ drm_gem_prime_fd_to_handle() helper functions as their PRIME operations<br>
+ handlers.<br>
+<br></blockquote><div><br></div><div><br></div><div>editorial nag: maybe move the notes about dma-buf / cross-device / cross-subsystem stuff above the prime_x_to_y fxn ptrs.. the real purpose of drm_gem_prime_x_to_y() was to allow dma-buf/prime to be used by drivers that do not use GEM. Any driver using GEM in some form or another, either by itself or in combination w/ TTM, probably wants to use drm_gem_prime_x_to_y() for these two fxn ptrs and instead provide the .gem_prime_{import,export} fxn ptr hooks which are used by drm_gem_prime_x_to_y(). </div>
<div><br></div><div>But if your buffer object handle is not a GEM handle, then you instead need to implement .prime_x_to_y() fxn ptrs.. I think this applies only just to one driver (vmwgfx? I don't remember for sure, airlied mentioned it at one point). This was the reason for the split into two sets of fxn ptrs, so that non GEM drivers don't get left out.</div>
<div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ The dma-buf PRIME helpers rely on the driver .gem_prime_export() and<br>
+ .gem_prime_import() operations to create a dma-buf instance from a GEM<br>
+ object (exporter role) and to create a GEM object from a dma-buf instance<br>
+ (importer role). These two operations are mandatory when using dma-buf with<br>
+ DRM PRIME.<br>
+<br></blockquote><div><br></div><div>only mandatory when using GEM+prime</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+- GEM Objects Mapping<br>
+<br>
+ Because mapping operations are fairly heavyweight GEM favours read/write-<br>
+ like access to buffers, implemented through driver-specific ioctls, over<br>
+ mapping buffers to userspace. However, when random access to the buffer is<br>
+ needed (to perform software rendering for instance), direct access to the<br>
+ object can be more efficient.<br>
+<br>
+ The mmap system call can't be used directly to map GEM objects, as they<br>
+ don't have their own file handle. Two alternative methods currently co-exist<br>
+ to map GEM objects to userspace. The first method uses a driver-specific<br>
+ ioctl to perform the mapping operation, calling do_mmap() under the hood.<br>
+ This is often considered dubious, seems to be discouraged for new<br>
+ GEM-enabled driver, and will thus not be described here.<br>
+<br>
+ The second method uses the mmap system call on the DRM file handle.<br>
+<br>
+ void *mmap(void *addr, size_t length, int prot, int flags, int fd,<br>
+ off_t offset)<br>
+<br>
+ DRM identifies the GEM object to be mapped by a fake offset passed through<br>
+ the mmap offset argument. Prior to being mapped, a GEM object must thus be<br>
+ associated with a fake offset. To do so, drivers must call<br>
+ drm_gem_create_mmap_offset() on the object. The function allocates a fake<br>
+ offset range from a pool and stores the offset divided by PAGE_SIZE in<br>
+ obj->map_list.hash.key. Care must be taken not to call<br>
+ drm_gem_create_mmap_offset() if a fake offset has already been allocated for<br>
+ the object. This can be tested by obj->map_list.map being non-NULL.<br>
+<br>
+ Once allocated, the fake offset value (obj->map_list.hash.key << PAGE_SHIFT)<br>
+ must be passed to the application in a driver-specific way and can then be<br>
+ used as the mmap offset argument.<br>
+<br>
+ The GEM core provides a helper method drm_gem_mmap() to handle object<br>
+ mapping. The method can be set directly as the mmap file operation handler.<br>
+ It will look up the GEM object based on the offset value and set the VMA<br>
+ operations to the drm_driver gem_vm_ops field. Note that drm_gem_mmap()<br>
+ doesn't map memory to userspace, but relies on the driver-provided fault<br>
+ handler to map pages individually.<br>
+<br>
+ To use drm_gem_mmap(), drivers must fill the struct drm_driver gem_vm_ops<br>
+ field with a pointer to VM operations.<br>
+<br>
+ struct vm_operations_struct *gem_vm_ops<br>
+<br>
+ struct vm_operations_struct {<br>
+ void (*open)(struct vm_area_struct * area);<br>
+ void (*close)(struct vm_area_struct * area);<br>
+ int (*fault)(struct vm_area_struct *vma, struct vm_fault *vmf);<br>
+ }<br>
+<br>
+ The open and close operations must update the GEM object reference count.<br>
+ Drivers can use the drm_gem_vm_open() and drm_gem_vm_close() helper<br>
+ functions directly as open and close handlers.<br>
+<br>
+ The fault operation handler is responsible for mapping individual pages to<br>
+ userspace when a page fault occurs. Depending on the memory allocation<br>
+ scheme, drivers can allocate pages at fault time, or can decide to allocate<br>
+ memory for the GEM object at the time the object is created.<br>
+<br>
+ Drivers that want to map the GEM object upfront instead of handling page<br>
+ faults can implement their own mmap file operation handler.<br>
+<br>
+- Dumb GEM Objects<br>
+<br>
+ The GEM API doesn't standardize GEM objects creation and leaves it to<br>
+ driver-specific ioctls. While not an issue for full-fledged graphics stacks<br>
+ that include device-specific userspace components (in libdrm for instance),<br>
+ this limit makes DRM-based early boot graphics unnecessarily complex.<br>
+<br>
+ Dumb GEM objects partly alleviate the problem by providing a standard API to<br>
+ create dumb buffers suitable for scanout, which can then be used to create<br>
+ KMS frame buffers.<br>
+<br>
+ To support dumb GEM objects drivers must implement the .dumb_create(),<br>
+ .dumb_destroy() and .dumb_map_offset() operations.<br>
+<br>
+ int (*dumb_create)(struct drm_file *file_priv, struct drm_device *dev,<br>
+ struct drm_mode_create_dumb *args)<br>
+<br>
+ The .dumb_create() operation creates a GEM object suitable for scanout based<br>
+ on the width, height and depth from the struct drm_mode_create_dumb<br>
+ argument. It fills the argument's handle, pitch and size fields with a<br>
+ handle for the newly created GEM object and its line pitch and size in<br>
+ bytes.<br>
+<br>
+ int (*dumb_destroy)(struct drm_file *file_priv, struct drm_device *dev,<br>
+ uint32_t handle)<br>
+<br>
+ The .dumb_destroy() operation destroys a dumb GEM object created by<br>
+ .dumb_create().<br>
+<br>
+ int (*dumb_map_offset)(struct drm_file *file_priv, struct drm_device *dev,<br>
+ uint32_t handle, uint64_t *offset)<br>
+<br>
+ The .dumb_map_offset() operation associates an mmap fake offset with the GEM<br>
+ object given by the handle and returns it. Drivers must use the<br>
+ drm_gem_create_mmap_offset() function to associate the fake offset as<br>
+ described in the GEM Objects Mapping section.<br>
+<br>
+<br>
+7. Mid-layer<br>
+------------<br>
+<br>
+The CRTC, encoder and connector functions provided by the drivers implement<br>
+the DRM API. They're called by the DRM core and ioctl handlers to handle<br>
+device state changes and configuration request. As implementing those<br>
+functions often requires logic not specific to drivers, mid-layer helper<br>
+functions are available to avoid duplicating boilerplate code.<br>
+<br>
+The DRM core contains one mid-layer implementation. The mid-layer provides<br>
+implementations of several CRTC, encoder and connector functions (called from<br>
+the top of the mid-layer) that pre-process requests and call lower-level<br>
+functions provided by the driver (at the bottom of the mid-layer). For<br>
+instance, the drm_crtc_helper_set_config() function can be used to fill the<br>
+struct drm_crtc_funcs set_config field. When called, it will split the<br>
+set_config operation in smaller, simpler operations and call the driver to<br>
+handle them.<br>
+<br>
+To use the mid-layer, drivers call drm_crtc_helper_add(),<br>
+drm_encoder_helper_add() and drm_connector_helper_add() functions to install<br>
+their mid-layer bottom operations handlers, and fill the drm_crtc_funcs,<br>
+drm_encoder_funcs and drm_connector_funcs structures with pointers to the<br>
+mid-layer top API functions. Installing the mid-layer bottom operation<br>
+handlers is best done right after registering the corresponding KMS object.<br>
+<br>
+The mid-layer is not split between CRTC, encoder and connector operations. To<br>
+use it, a driver must provide bottom functions for all of the three KMS<br>
+entities.<br>
+<br>
+<br>
+8. Mode Setting Operations<br>
+--------------------------<br>
+<br>
+- struct drm_framebuffer *(*fb_create)(struct drm_device *dev,<br>
+ struct drm_file *file_priv,<br>
+ struct drm_mode_fb_cmd2 *mode_cmd)<br>
+<br>
+ Create a new frame buffer.<br>
+<br>
+ Frame buffers are abstract memory objects that provide a source of pixels to<br>
+ scanout to a CRTC. Applications explicitly request the creation of frame<br>
+ buffers through the DRM_IOCTL_MODE_ADDFB(2) ioctls and receive an opaque<br>
+ handle that can be passed to the KMS CRTC control, plane configuration and<br>
+ page flip functions.<br>
+<br>
+ Frame buffers rely on the underneath memory manager for low-level memory<br>
+ operations. When creating a frame buffer applications pass a memory handle<br>
+ (or a list of memory handles for multi-planar formats) through the<br>
+ drm_mode_fb_cmd2 argument. This document assumes that the driver uses GEM,<br>
+ those handles thus reference GEM objects.<br>
+<br>
+ Drivers must first validate the requested frame buffer parameters passed<br>
+ through the mode_cmd argument. In particular this is where invalid sizes,<br>
+ pixel formats or pitches can be caught.<br>
+<br>
+ If the parameters are deemed valid, drivers then create, initialize and<br>
+ return an instance of struct drm_framebuffer. If desired the instance can be<br>
+ embedded in a larger driver-specific structure. The new instance is<br>
+ initialized with a call to drm_framebuffer_init() which takes a pointer to<br>
+ DRM frame buffer operations (struct drm_framebuffer_funcs). Frame buffer<br>
+ operations are<br>
+<br>
+ - int (*create_handle)(struct drm_framebuffer *fb,<br>
+ struct drm_file *file_priv, unsigned int *handle)<br>
+<br>
+ Create a handle to the frame buffer underlying memory object. If the frame<br>
+ buffer uses a multi-plane format, the handle will reference the memory<br>
+ object associated with the first plane.<br>
+<br>
+ Drivers call drm_gem_handle_create() to create the handle.<br>
+<br>
+ - void (*destroy)(struct drm_framebuffer *framebuffer)<br>
+<br>
+ Destroy the frame buffer object and frees all associated resources.<br>
+ Drivers must call drm_framebuffer_cleanup() to free resources allocated by<br>
+ the DRM core for the frame buffer object, and must make sure to<br>
+ unreference all memory objects associated with the frame buffer. Handles<br>
+ created by the .create_handle() operation are released by the DRM core.<br>
+<br>
+ - int (*dirty)(struct drm_framebuffer *framebuffer,<br>
+ struct drm_file *file_priv, unsigned flags, unsigned color,<br>
+ struct drm_clip_rect *clips, unsigned num_clips)<br>
+<br>
+ This optional operation notifies the driver that a region of the frame<br>
+ buffer has changed in response to a DRM_IOCTL_MODE_DIRTYFB ioctl call.<br>
+<br>
+ After initializing the drm_framebuffer instance drivers must fill its width,<br>
+ height, pitches, offsets, depth, bits_per_pixel and pixel_format fields from<br>
+ the values passed through the drm_mode_fb_cmd2 argument. They should call<br>
+ the drm_helper_mode_fill_fb_struct() helper function to do so.<br>
+<br>
+- void (*output_poll_changed)(struct drm_device *dev)<br>
+<br>
+ This operation notifies the driver that the status of one or more connectors<br>
+ has changed. Drivers that use the fbdev helper can just call the<br>
+ drm_fb_helper_hotplug_event() function to handle this operation.<br>
+<br>
+<br>
+9. CRTC Operations<br>
+-------------------<br>
+<br>
+- void (*gamma_set)(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,<br>
+ uint32_t start, uint32_t size)<br>
+<br>
+ Apply a gamma table to the device. The operation is optional.<br>
+<br>
+- void (*destroy)(struct drm_crtc *crtc)<br>
+<br>
+ Destroy the CRTC when not needed anymore. See the KMS cleanup section.<br>
+<br>
+- int (*set_config)(struct drm_mode_set *set)<br>
+<br>
+ Apply a new CRTC configuration to the device. The configuration specifies a<br>
+ CRTC, a frame buffer to scan out from, a (x,y) position in the frame buffer,<br>
+ a display mode and an array of connectors to drive with the CRTC if<br>
+ possible.<br>
+<br>
+ If the frame buffer specified in the configuration is NULL, the driver must<br>
+ detach all encoders connected to the CRTC and all connectors attached to<br>
+ those encoders and disable them.<br>
+<br>
+ This operation is called with the mode config lock held.<br>
+<br>
+ (FIXME: How should set_config interact with DPMS? If the CRTC is suspended,<br>
+ should it be resumed?)<br></blockquote><div><br></div><div><br></div><div>DPMS calls from userspace are made on the connector, and internally propagated to encoder/crtc/etc via drm_helper_connector_dpms(). When it is resumed, it should (IIRC) keep it's old settings.</div>
<div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ The mid-layer provides a drm_crtc_helper_set_config() helper function. The<br>
+ helper will try to locate the best encoder for each connector by calling the<br>
+ connector .best_encoder helper operation. That operation is mandatory and<br>
+ must return a pointer to the best encoder for the connector. For devices<br>
+ that map connectors to encoders 1:1, the function simply returns the pointer<br>
+ to the associated encoder.<br>
+<br>
+ After locating the appropriate encoders, the helper function will call the<br>
+ mandatory mode_fixup encoder and CRTC helper operations.<br>
+<br>
+ - bool (*mode_fixup)(struct drm_encoder *encoder,<br>
+ const struct drm_display_mode *mode,<br>
+ struct drm_display_mode *adjusted_mode)<br>
+ - bool (*mode_fixup)(struct drm_crtc *crtc,<br>
+ const struct drm_display_mode *mode,<br>
+ struct drm_display_mode *adjusted_mode)<br>
+<br>
+ (FIXME: Should the mode argument be const? The i915 driver modifies<br>
+ mode->clock in intel_dp_mode_fixup().)<br>
+<br>
+ Let encoders and CRTC adjust the requested mode or reject it completely.<br>
+ Those operations return true if the mode is accepted (possibly after being<br>
+ adjusted) or false if it is rejected.<br>
+<br>
+ The mode_fixup operation should reject the mode if it can't reasonably use<br>
+ it. The definition of "reasonable" is currently fuzzy in this context. One<br>
+ possible behaviour would be to set the adjusted mode to the panel timings<br>
+ when a fixed-mode panel is used with hardware capable of scaling. Anothe<br></blockquote><div><br></div><div>s/Anothe/Another/</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+ behaviour would be to accept any input mode and adjust it to the closest<br>
+ mode supported by the hardware (FIXME: This needs to be clarified).<br>
+<br>
+ If the new configuration after mode adjustment is identical to the current<br>
+ configuration the helper function will return without performing any other<br>
+ operation.<br>
+<br>
+ If the adjusted mode is identical to the current mode but changes to the<br>
+ frame buffer need to be applied, the drm_crtc_helper_set_config() function<br>
+ will call the CRTC .mode_set_base() helper operation.<br>
+<br>
+ - int (*mode_set_base)(struct drm_crtc *crtc, int x, int y,<br>
+ struct drm_framebuffer *old_fb)<br>
+<br>
+ Move the CRTC on the current frame buffer (stored in crtc->fb) to position<br>
+ (x,y). Any of the frame buffer, x position or y position may have been<br>
+ modified.<br>
+<br>
+ This helper operation is optional. If not provided, the<br>
+ drm_crtc_helper_set_config() function will fall back to the .mode_set()<br>
+ helper operation.<br>
+<br>
+ (FIXME: Why are x and y passed as arguments, as they can be accessed<br>
+ through crtc->x and crtc->y?)<br>
+<br>
+ If the adjusted mode differs from the current mode, or if the<br>
+ .mode_set_base() helper operation is not provided, the helper function<br>
+ performs a full mode set sequence by calling the following mandatory<br>
+ CRTC and encoder operations in order.<br>
+<br>
+ - void (*prepare)(struct drm_encoder *encoder)<br>
+ - void (*prepare)(struct drm_crtc *crtc)<br>
+<br>
+ Those operations are called after validating the requested mode. Drivers<br>
+ use them to perform device-specific operations required before setting the<br>
+ new mode.<br>
+<br>
+ - int (*mode_set)(struct drm_crtc *crtc, struct drm_display_mode *mode,<br>
+ struct drm_display_mode *adjusted_mode, int x, int y,<br>
+ struct drm_framebuffer *old_fb)<br>
+ - void (*mode_set)(struct drm_encoder *encoder,<br>
+ struct drm_display_mode *mode,<br>
+ struct drm_display_mode *adjusted_mode)<br>
+<br>
+ Those operations set the new mode. Depending on the device requirements,<br>
+ the mode can be stored internally by the driver and applied in the commit<br>
+ operations, or programmed to the hardware here.<br>
+<br>
+ The crtc::mode_set operation returns 0 on success or a negative error code<br>
+ if an error occurs. The encoder::mode_set operation isn't allowed to fail.<br>
+<br>
+ - void (*commit)(struct drm_crtc *crtc)<br>
+ - void (*commit)(struct drm_encoder *encoder)<br>
+<br>
+ Those operations are called after setting the new mode. Upon return the<br>
+ device must use the new mode and be fully operational.<br>
+<br>
+- int (*page_flip)(struct drm_crtc *crtc, struct drm_framebuffer *fb,<br>
+ struct drm_pending_vblank_event *event)<br>
+<br>
+ Schedule a page flip to the given frame buffer for the CRTC. This operation<br>
+ is called with the mode config mutex held.<br>
+<br>
+ Page flipping is a synchronization mechanism that replaces the frame buffer<br>
+ being scanned out by the CRTC with a new frame buffer during vertical<br>
+ blanking, avoiding tearing. When an application requests a page flip the DRM<br>
+ core verifies that the new frame buffer is large enough to be scanned out by<br>
+ the CRTC in the currently configured mode and then calls the CRTC<br>
+ .page_flip() operation with a pointer to the new frame buffer.<br>
+<br>
+ The .page_flip() operation schedules a page flip. Once any pending rendering<br>
+ targetting the new frame buffer has completed, the CRTC will be reprogrammed<br>
+ to display that frame buffer after the next vertical refresh. The operation<br>
+ must return immediately without waiting for rendering or page flip to<br>
+ complete and must block any new rendering to the frame buffer until the page<br>
+ flip completes.<br>
+<br>
+ If a page flip is already pending, the .page_flip() operation must return<br>
+ -EBUSY.<br>
+<br>
+ (FIXME: Should DRM allow queueing multiple page flips?)<br></blockquote><div><br></div><div><br></div><div>Queuing could be done in userspace (which gets a page_flip event when flip completes).. this at least simplifies the driver somewhat. And in cases where UI interactivity is required you really don't want to let rendering get too far ahead of scanout or the user will see this as laggy display. So I don't see a particularly good reason to support queuing.</div>
<div><br></div><div>(Also, makes it easier to move page_flip event handling to the core if you can have only at most one outstanding page flip.)</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ To synchronize page flip to vertical blanking the driver will likely need to<br>
+ enable vertical blanking interrupts. It should call drm_vblank_get() for<br>
+ that purpose, and call drm_vblank_put() after the page flip completes.<br>
+<br>
+ If the application has requested to be notified when page flip completes the<br>
+ .page_flip() operation will be called with a non-NULL event argument<br>
+ pointing to a drm_pending_vblank_event instance. Upon page flip completion<br>
+ the driver must fill the event::event sequence, tv_sec and tv_usec fields<br>
+ with the associated vertical blanking count and timestamp, add the event to<br>
+ the drm_file list of events to be signaled, and wake up any waiting process.<br>
+ This can be performed with<br>
+<br>
+ struct timeval now;<br>
+<br>
+ event->event.sequence = drm_vblank_count_and_time(..., &now);<br>
+ event->event.tv_sec = now.tv_sec;<br>
+ event->event.tv_usec = now.tv_usec;<br>
+<br>
+ spin_lock_irqsave(&dev->event_lock, flags);<br>
+ list_add_tail(&event->base.link, &event->base.file_priv->event_list);<br>
+ wake_up_interruptible(&event->base.file_priv->event_wait);<br>
+ spin_unlock_irqrestore(&dev->event_lock, flags);<br>
+<br>
+ (FIXME: Could drivers that don't need to wait for rendering to complete just<br>
+ add the event to dev->vblank_event_list and let the DRM core handle<br>
+ everything, as for "normal" vertical blanking events?)<br></blockquote><div><br></div><div>Or just drm_crtc_handle_page_flip_event() which would also let us simplify some of the cleanup when userspace closes the device, as mentioned earlier ;-) I think when I have a few spare minutes I'll try this.</div>
<div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+<br>
+ While waiting for the page flip to complete, the event->base.link list head<br>
+ can be used freely by the driver to store the pending event in a<br>
+ driver-specific list.<br>
+<br>
+ If the file handle is closed before the event is signaled, drivers must take<br>
+ care to destroy the event in their .preclose() operation (and, if needed,<br>
+ call drm_vblank_put()).<br>
+<br>
+<br>
+10. Plane Operations<br>
+-------------------<br>
+<br>
+- int (*update_plane)(struct drm_plane *plane, struct drm_crtc *crtc,<br>
+ struct drm_framebuffer *fb, int crtc_x, int crtc_y,<br>
+ unsigned int crtc_w, unsigned int crtc_h,<br>
+ uint32_t src_x, uint32_t src_y,<br>
+ uint32_t src_w, uint32_t src_h)<br>
+<br>
+ Enable and configure the plane to use the given CRTC and frame buffer.<br>
+<br>
+ The source rectangle in frame buffer memory coordinates is given by the<br>
+ src_x, src_y, src_w and src_h parameters (as 16.16 fixed point values).<br>
+ Devices that don't support subpixel plane coordinates can ignore the<br>
+ fractional part.<br>
+<br>
+ The destination rectangle in CRTC coordinates is given by the crtc_x,<br>
+ crtc_y, crtc_w and crtc_h parameters (as integer values). Devices scale<br>
+ the source rectangle to the destination rectangle. If scaling is not<br>
+ supported, the src_w and src_h values can be ignored.<br></blockquote><div><br></div><div><br></div><div>hmm, ignored seems bad.. maybe driver should -EINVAL?</div><div><br></div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
+- int (*disable_plane)(struct drm_plane *plane)<br>
+<br>
+ Disable the plane. The DRM core calls this method in response to a<br>
+ DRM_IOCTL_MODE_SETPLANE ioctl call with the frame buffer ID set to 0.<br>
+ Disabled planes must not be processed by the CRTC.<br>
+<br>
+- void (*destroy)(struct drm_plane *plane)<br>
+<br>
+ Destroy the plane when not needed anymore. See the KMS cleanup section.<br>
+<br>
+<br>
+11. Encoder Operations<br>
+----------------------<br>
+<br>
+- void (*destroy)(struct drm_encoder *encoder)<br>
+<br>
+ Called to destroy the encoder when not needed anymore. See the KMS cleanup<br>
+ section.<br>
+<br>
+<br>
+12. Connector Operations<br>
+------------------------<br>
+<br>
+Unless otherwise state, all operations are mandatory.<br>
+<br>
+- status - connection status (connected, disconnected, unknown)<br>
+<br>
+ The connection status is updated through polling or hotplug events when<br>
+ supported (see the polled field description). The status value is reported<br>
+ to userspace through ioctls and must not be used inside the driver, as it<br>
+ only gets initialized by a call to drm_mode_getconnector() from userspace.<br>
+<br>
+- void (*dpms)(struct drm_connector *connector, int mode)<br>
+<br>
+ The DPMS operation sets the power state of a connector. The mode argument is<br>
+ one of<br>
+<br>
+ DRM_MODE_DPMS_ON<br>
+ DRM_MODE_DPMS_STANDBY<br>
+ DRM_MODE_DPMS_SUSPEND<br>
+ DRM_MODE_DPMS_OFF<br>
+<br>
+ In all but DPMS_ON mode the encoder to which the connector is attached<br>
+ should put the display in low-power mode by driving its signals appropriately.<br>
+ If more than one connector is attached to the encoder care should be taken<br>
+ not to change the power state of other displays as a side effect. Low-power<br>
+ mode should be propagated to the encoders and CRTCs when all related<br>
+ connectors are put in low-power mode.<br>
+<br>
+ The mid-layer offers a drm_helper_connector_dpms() helper function that<br>
+ tracks power state of connectors. When using the helper function drivers<br>
+ only need to provide .dpms helper operations for CRTCs and encoders to apply<br>
+ the DPMS state to the device.<br>
+<br>
+ The mid-layer doesn't track the power state of CRTCs and encoders. The .dpms<br>
+ operations can thus be called with a mode identical to the currently active<br>
+ mode.<br>
+<br>
+- enum drm_connector_status (*detect)(struct drm_connector *connector,<br>
+ bool force)<br>
+<br>
+ Check to see if anything is attached to the connector. @force is set to<br>
+ false whilst polling, true when checking the connector due to user request.<br>
+ @force can be used by the driver to avoid expensive, destructive operations<br>
+ during automated probing.<br>
+<br>
+ Return connector_status_connected if something is connected to the<br>
+ connector, connector_status_disconnected if nothing is connected and<br>
+ connector_status_unknown if the connection state isn't known.<br>
+<br>
+ Drivers should only return connector_status_connected if the connection<br>
+ status has really been probed as connected. Connectors that can't detect the<br>
+ connection status, or failed connection status probes, should return<br>
+ connector_status_unknown.<br>
+<br>
+- int (*fill_modes)(struct drm_connector *connector, uint32_t max_width,<br>
+ uint32_t max_height)<br>
+<br>
+ Fill the mode list with all supported modes for the connector. If the<br>
+ max_width and max_height arguments are non-zero, the implementation must<br>
+ ignore all modes wider than max_width or higher than max_height.<br>
+<br>
+ The connector must also fill in this operation its display_info width_mm and<br>
+ height_mm fields with the connected display physical size in millimeters.<br>
+ The fields should be set to 0 if the value isn't known or is not applicable<br>
+ (for instance for projector devices).<br>
+<br>
+ The mid-layer provides a drm_helper_probe_single_connector_modes() helper<br>
+ function. The helper updates the connection status for the connector and<br>
+ then retrieves a list of modes by calling the connector .get_modes helper<br>
+ operation.<br>
+<br>
+ The .get_modes helper operation is mandatory. It must fill the connector's<br>
+ probed_modes list by parsing EDID data with drm_add_edid_modes() or calling<br>
+ drm_mode_probed_add() directly for every supported mode. The operation<br>
+ returns the number of modes it has detected.<br>
+<br>
+ When adding modes manually the driver creates each mode with a call to<br>
+ drm_mode_create() and must fill the following fields.<br>
+<br>
+ - type: Mode type bitmask, a combination of<br>
+<br>
+ DRM_MODE_TYPE_BUILTIN - not used?<br>
+ DRM_MODE_TYPE_CLOCK_C - not used?<br>
+ DRM_MODE_TYPE_CRTC_C - not used?<br>
+ DRM_MODE_TYPE_PREFERRED - The preferred mode for the connector<br>
+ DRM_MODE_TYPE_DEFAULT - not used?<br>
+ DRM_MODE_TYPE_USERDEF - not used?<br>
+ DRM_MODE_TYPE_DRIVER - The mode has been created by the driver (as opposed<br>
+ to user-created modes)<br>
+<br>
+ Drivers must set the DRM_MODE_TYPE_DRIVER bit for all modes they create,<br>
+ and set the DRM_MODE_TYPE_PREFERRED bit for the preferred mode.<br>
+<br>
+ - clock: Pixel clock frequency in kHz unit<br>
+<br>
+ - hdisplay, hsync_start, hsync_end, htotal: Horizontal timing information<br>
+ - vdisplay, vsync_start, vsync_end, vtotal: Vertical timing information<br>
+<br>
+ Active Front Sync Back<br>
+ Region Porch Porch<br>
+ <-----------------------><----------------><-------------><--------------><br>
+<br>
+ //////////////////////|<br>
+ ////////////////////// |<br>
+ ////////////////////// |.................. ................<br>
+ _______________<br>
+<br>
+ <----- [hv]display -----><br>
+ <------------- [hv]sync_start ------------><br>
+ <--------------------- [hv]sync_end ---------------------><br>
+ <-------------------------------- [hv]total -----------------------------><br>
+<br>
+ - hskew, vscan: ?<br>
+<br>
+ - flags: Mode flags, a combination of<br>
+<br>
+ DRM_MODE_FLAG_PHSYNC - Horizontal sync is active high<br>
+ DRM_MODE_FLAG_NHSYNC - Horizontal sync is active low<br>
+ DRM_MODE_FLAG_PVSYNC - Vertical sync is active high<br>
+ DRM_MODE_FLAG_NVSYNC - Vertical sync is active low<br>
+ DRM_MODE_FLAG_INTERLACE - Mode is interlaced<br>
+ DRM_MODE_FLAG_DBLSCAN - Mode uses doublescan<br>
+ DRM_MODE_FLAG_CSYNC - Mode uses composite sync<br>
+ DRM_MODE_FLAG_PCSYNC - Composite sync is active high<br>
+ DRM_MODE_FLAG_NCSYNC - Composite sync is active low<br>
+ DRM_MODE_FLAG_HSKEW - hskew provided (not used?)<br>
+ DRM_MODE_FLAG_BCAST - not used?<br>
+ DRM_MODE_FLAG_PIXMUX - not used?<br>
+ DRM_MODE_FLAG_DBLCLK - not used?<br>
+ DRM_MODE_FLAG_CLKDIV2 - ?<br>
+<br>
+ Note that modes marked with the INTERLACE or DBLSCAN flags will be<br>
+ filtered out by drm_helper_probe_single_connector_modes() if the<br>
+ connector's interlace_allowed or doublescan_allowed field is set to 0.<br>
+<br>
+ - name: Mode name<br>
+<br>
+ The driver must call drm_mode_set_name() to fill the mode name from the<br>
+ hdisplay, vdisplay and interlace flag after filling the corresponding<br>
+ fields.<br>
+<br>
+ The vrefresh value is computed by drm_helper_probe_single_connector_modes().<br>
+<br>
+ When parsing EDID data, drm_add_edid_modes() fill the connector display_info<br>
+ width_mm and height_mm fields. When creating modes manually the .get_modes<br>
+ helper operation must set the display_info width_mm and height_mm fields if<br>
+ they haven't been set already (for instance at initilization time when a<br>
+ fixed-size panel is attached to the connector). The mode width_mm and<br>
+ height_mm fields are only used internally during EDID parsing and should not<br>
+ be set when creating modes manually.<br>
+<br>
+ The helper function filters out modes larger than max_width and max_height<br>
+ if specified. It then calls the connector .mode_valid helper operation for<br>
+ each mode in the probed list to check whether the mode is valid for the<br>
+ connector. The helper is mandatory and returns MODE_OK for supported modes<br>
+ and one of the enum drm_mode_status values (MODE_*) for unsupported modes.<br>
+ As unsupported modes will be immediately removed an implementation can<br>
+ return MODE_BAD regardless of the exact reason why the mode is not valid.<br>
+<br>
+ Note that the .mode_valid helper operation is only called for modes detected<br>
+ by the device, and *not* for modes set by the user through the CRTC<br>
+ .set_config operation.<br>
+<br>
+- void (*destroy)(struct drm_connector *connector)<br>
+<br>
+ Destroy the connector when not needed anymore. See the KMS cleanup section.<br>
+<br>
+<br>
+13. TODO<br>
+--------<br>
+<br>
+- Document the struct_mutex catch-all lock<br>
+- Document connector properties<br>
+<br>
+- crtc and encoder dpms helper operations are only mandatory if the disable<br>
+ operation isn't provided.<br>
+- crtc and connector .save and .restore operations are only used internally in<br>
+ drivers, should they be removed from the core?<br>
+- encoder mid-layer .save and .restore operations are only used internally in<br>
+ drivers, should they be removed from the core?<br>
+- encoder mid-layer .detect operation is only used internally in drivers,<br>
+ should it be removed from the core?<br>
+<br>
+- KMS drivers must call drm_vblank_pre_modeset() and drm_vblank_post_modeset()<br>
+ around mode setting. Should this be done in the DRM core?<br>
+- vblank_disable_allowed is set to 1 in the first drm_vblank_post_modeset()<br>
+ call and never set back to 0. It seems to be safe to permanently set it to 1<br>
+ in drm_vblank_init() for KMS driver, and it might be safe for UMS drivers as<br>
+ well. This should be investigated.<br></blockquote><div><br></div><div><br></div><div>cool, thx.. I may have missed some things since I was trying to hurry up and finish reviewing before my battery died ;-)</div><div>
<br></div><div>BR,</div><div>-R</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
--<br>
Regards,<br>
<br>
Laurent Pinchart<br>
<br>
_______________________________________________<br>
dri-devel mailing list<br>
<a>dri-devel@lists.freedesktop.org</a><br>
<a href="http://lists.freedesktop.org/mailman/listinfo/dri-devel" target="_blank">http://lists.freedesktop.org/mailman/listinfo/dri-devel</a><br>
</blockquote></div>