| <?xml version="1.0" encoding="UTF-8"?> | |
| <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" | |
| "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []> | |
| <book id="drmDevelopersGuide"> | |
| <bookinfo> | |
| <title>Linux DRM Developer's Guide</title> | |
| <copyright> | |
| <year>2008-2009</year> | |
| <holder> | |
| Intel Corporation (Jesse Barnes <jesse.barnes@intel.com>) | |
| </holder> | |
| </copyright> | |
| <legalnotice> | |
| <para> | |
| The contents of this file may be used under the terms of the GNU | |
| General Public License version 2 (the "GPL") as distributed in | |
| the kernel source COPYING file. | |
| </para> | |
| </legalnotice> | |
| </bookinfo> | |
| <toc></toc> | |
| <!-- Introduction --> | |
| <chapter id="drmIntroduction"> | |
| <title>Introduction</title> | |
| <para> | |
| The Linux DRM layer contains code intended to support the needs | |
| of complex graphics devices, usually containing programmable | |
| pipelines well suited to 3D graphics acceleration. Graphics | |
| drivers in the kernel can make use of DRM functions to make | |
| tasks like memory management, interrupt handling and DMA easier, | |
| and provide a uniform interface to applications. | |
| </para> | |
| <para> | |
| A note on versions: this guide covers features found in the DRM | |
| tree, including the TTM memory manager, output configuration and | |
| mode setting, and the new vblank internals, in addition to all | |
| the regular features found in current kernels. | |
| </para> | |
| <para> | |
| [Insert diagram of typical DRM stack here] | |
| </para> | |
| </chapter> | |
| <!-- Internals --> | |
| <chapter id="drmInternals"> | |
| <title>DRM Internals</title> | |
| <para> | |
| This chapter documents DRM internals relevant to driver authors | |
| and developers working to add support for the latest features to | |
| existing drivers. | |
| </para> | |
| <para> | |
| First, we'll go over some typical driver initialization | |
| requirements, like setting up command buffers, creating an | |
| initial output configuration, and initializing core services. | |
| Subsequent sections will cover core internals in more detail, | |
| providing implementation notes and examples. | |
| </para> | |
| <para> | |
| The DRM layer provides several services to graphics drivers, | |
| many of them driven by the application interfaces it provides | |
| through libdrm, the library that wraps most of the DRM ioctls. | |
| These include vblank event handling, memory | |
| management, output management, framebuffer management, command | |
| submission & fencing, suspend/resume support, and DMA | |
| services. | |
| </para> | |
| <para> | |
| The core of every DRM driver is struct drm_driver. Drivers | |
| will typically statically initialize a drm_driver structure, | |
| then pass it to drm_init() at load time. | |
| </para> | |
| <!-- Internals: driver init --> | |
| <sect1> | |
| <title>Driver initialization</title> | |
| <para> | |
| Before calling the DRM initialization routines, the driver must | |
| first create and fill out a struct drm_driver structure. | |
| </para> | |
| <programlisting> | |
| static struct drm_driver driver = { | |
| /* don't use mtrr's here, the Xserver or user space app should | |
| * deal with them for intel hardware. | |
| */ | |
| .driver_features = | |
| DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | | |
| DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_MODESET, | |
| .load = i915_driver_load, | |
| .unload = i915_driver_unload, | |
| .firstopen = i915_driver_firstopen, | |
| .lastclose = i915_driver_lastclose, | |
| .preclose = i915_driver_preclose, | |
| .save = i915_save, | |
| .restore = i915_restore, | |
| .device_is_agp = i915_driver_device_is_agp, | |
| .get_vblank_counter = i915_get_vblank_counter, | |
| .enable_vblank = i915_enable_vblank, | |
| .disable_vblank = i915_disable_vblank, | |
| .irq_preinstall = i915_driver_irq_preinstall, | |
| .irq_postinstall = i915_driver_irq_postinstall, | |
| .irq_uninstall = i915_driver_irq_uninstall, | |
| .irq_handler = i915_driver_irq_handler, | |
| .reclaim_buffers = drm_core_reclaim_buffers, | |
| .get_map_ofs = drm_core_get_map_ofs, | |
| .get_reg_ofs = drm_core_get_reg_ofs, | |
| .fb_probe = intelfb_probe, | |
| .fb_remove = intelfb_remove, | |
| .fb_resize = intelfb_resize, | |
| .master_create = i915_master_create, | |
| .master_destroy = i915_master_destroy, | |
| #if defined(CONFIG_DEBUG_FS) | |
| .debugfs_init = i915_debugfs_init, | |
| .debugfs_cleanup = i915_debugfs_cleanup, | |
| #endif | |
| .gem_init_object = i915_gem_init_object, | |
| .gem_free_object = i915_gem_free_object, | |
| .gem_vm_ops = &i915_gem_vm_ops, | |
| .ioctls = i915_ioctls, | |
| .fops = { | |
| .owner = THIS_MODULE, | |
| .open = drm_open, | |
| .release = drm_release, | |
| .ioctl = drm_ioctl, | |
| .mmap = drm_mmap, | |
| .poll = drm_poll, | |
| .fasync = drm_fasync, | |
| #ifdef CONFIG_COMPAT | |
| .compat_ioctl = i915_compat_ioctl, | |
| #endif | |
| .llseek = noop_llseek, | |
| }, | |
| .pci_driver = { | |
| .name = DRIVER_NAME, | |
| .id_table = pciidlist, | |
| .probe = probe, | |
| .remove = __devexit_p(drm_cleanup_pci), | |
| }, | |
| .name = DRIVER_NAME, | |
| .desc = DRIVER_DESC, | |
| .date = DRIVER_DATE, | |
| .major = DRIVER_MAJOR, | |
| .minor = DRIVER_MINOR, | |
| .patchlevel = DRIVER_PATCHLEVEL, | |
| }; | |
| </programlisting> | |
| <para> | |
| In the example above, taken from the i915 DRM driver, the driver | |
| sets several flags indicating what core features it supports. | |
| We'll go over the individual callbacks in later sections. Since | |
| flags indicate which features your driver supports to the DRM | |
| core, you need to set most of them prior to calling drm_init(). Some, | |
| like DRIVER_MODESET can be set later based on user supplied parameters, | |
| but that's the exception rather than the rule. | |
| </para> | |
| <variablelist> | |
| <title>Driver flags</title> | |
| <varlistentry> | |
| <term>DRIVER_USE_AGP</term> | |
| <listitem><para> | |
| Driver uses AGP interface | |
| </para></listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_REQUIRE_AGP</term> | |
| <listitem><para> | |
| Driver needs AGP interface to function. | |
| </para></listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_USE_MTRR</term> | |
| <listitem> | |
| <para> | |
| Driver uses MTRR interface for mapping memory. Deprecated. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_PCI_DMA</term> | |
| <listitem><para> | |
| Driver is capable of PCI DMA. Deprecated. | |
| </para></listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_SG</term> | |
| <listitem><para> | |
| Driver can perform scatter/gather DMA. Deprecated. | |
| </para></listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_HAVE_DMA</term> | |
| <listitem><para>Driver supports DMA. Deprecated.</para></listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_HAVE_IRQ</term><term>DRIVER_IRQ_SHARED</term> | |
| <listitem> | |
| <para> | |
| DRIVER_HAVE_IRQ indicates whether the driver has a IRQ | |
| handler, DRIVER_IRQ_SHARED indicates whether the device & | |
| handler support shared IRQs (note that this is required of | |
| PCI drivers). | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_DMA_QUEUE</term> | |
| <listitem> | |
| <para> | |
| If the driver queues DMA requests and completes them | |
| asynchronously, this flag should be set. Deprecated. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_FB_DMA</term> | |
| <listitem> | |
| <para> | |
| Driver supports DMA to/from the framebuffer. Deprecated. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| <varlistentry> | |
| <term>DRIVER_MODESET</term> | |
| <listitem> | |
| <para> | |
| Driver supports mode setting interfaces. | |
| </para> | |
| </listitem> | |
| </varlistentry> | |
| </variablelist> | |
| <para> | |
| In this specific case, the driver requires AGP and supports | |
| IRQs. DMA, as we'll see, is handled by device specific ioctls | |
| in this case. It also supports the kernel mode setting APIs, though | |
| unlike in the actual i915 driver source, this example unconditionally | |
| exports KMS capability. | |
| </para> | |
| </sect1> | |
| <!-- Internals: driver load --> | |
| <sect1> | |
| <title>Driver load</title> | |
| <para> | |
| In the previous section, we saw what a typical drm_driver | |
| structure might look like. One of the more important fields in | |
| the structure is the hook for the load function. | |
| </para> | |
| <programlisting> | |
| static struct drm_driver driver = { | |
| ... | |
| .load = i915_driver_load, | |
| ... | |
| }; | |
| </programlisting> | |
| <para> | |
| The load function has many responsibilities: allocating a driver | |
| private structure, specifying supported performance counters, | |
| configuring the device (e.g. mapping registers & command | |
| buffers), initializing the memory manager, and setting up the | |
| initial output configuration. | |
| </para> | |
| <para> | |
| Note that the tasks performed at driver load time must not | |
| conflict with DRM client requirements. For instance, if user | |
| level mode setting drivers are in use, it would be problematic | |
| to perform output discovery & configuration at load time. | |
| Likewise, if pre-memory management aware user level drivers are | |
| in use, memory management and command buffer setup may need to | |
| be omitted. These requirements are driver specific, and care | |
| needs to be taken to keep both old and new applications and | |
| libraries working. The i915 driver supports the "modeset" | |
| module parameter to control whether advanced features are | |
| enabled at load time or in legacy fashion. If compatibility is | |
| a concern (e.g. with drivers converted over to the new interfaces | |
| from the old ones), care must be taken to prevent incompatible | |
| device initialization and control with the currently active | |
| userspace drivers. | |
| </para> | |
| <sect2> | |
| <title>Driver private & performance counters</title> | |
| <para> | |
| The driver private hangs off the main drm_device structure and | |
| can be used for tracking various device specific bits of | |
| information, like register offsets, command buffer status, | |
| register state for suspend/resume, etc. At load time, a | |
| driver can simply allocate one and set drm_device.dev_priv | |
| appropriately; at unload the driver can free it and set | |
| drm_device.dev_priv to NULL. | |
| </para> | |
| <para> | |
| The DRM supports several counters which can be used for rough | |
| performance characterization. Note that the DRM stat counter | |
| system is not often used by applications, and supporting | |
| additional counters is completely optional. | |
| </para> | |
| <para> | |
| These interfaces are deprecated and should not be used. If performance | |
| monitoring is desired, the developer should investigate and | |
| potentially enhance the kernel perf and tracing infrastructure to export | |
| GPU related performance information to performance monitoring | |
| tools and applications. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Configuring the device</title> | |
| <para> | |
| Obviously, device configuration will be device specific. | |
| However, there are several common operations: finding a | |
| device's PCI resources, mapping them, and potentially setting | |
| up an IRQ handler. | |
| </para> | |
| <para> | |
| Finding & mapping resources is fairly straightforward. The | |
| DRM wrapper functions, drm_get_resource_start() and | |
| drm_get_resource_len() can be used to find BARs on the given | |
| drm_device struct. Once those values have been retrieved, the | |
| driver load function can call drm_addmap() to create a new | |
| mapping for the BAR in question. Note you'll probably want a | |
| drm_local_map_t in your driver private structure to track any | |
| mappings you create. | |
| <!-- !Fdrivers/gpu/drm/drm_bufs.c drm_get_resource_* --> | |
| <!-- !Finclude/drm/drmP.h drm_local_map_t --> | |
| </para> | |
| <para> | |
| if compatibility with other operating systems isn't a concern | |
| (DRM drivers can run under various BSD variants and OpenSolaris), | |
| native Linux calls can be used for the above, e.g. pci_resource_* | |
| and iomap*/iounmap. See the Linux device driver book for more | |
| info. | |
| </para> | |
| <para> | |
| Once you have a register map, you can use the DRM_READn() and | |
| DRM_WRITEn() macros to access the registers on your device, or | |
| use driver specific versions to offset into your MMIO space | |
| relative to a driver specific base pointer (see I915_READ for | |
| example). | |
| </para> | |
| <para> | |
| If your device supports interrupt generation, you may want to | |
| setup an interrupt handler at driver load time as well. This | |
| is done using the drm_irq_install() function. If your device | |
| supports vertical blank interrupts, it should call | |
| drm_vblank_init() to initialize the core vblank handling code before | |
| enabling interrupts on your device. This ensures the vblank related | |
| structures are allocated and allows the core to handle vblank events. | |
| </para> | |
| <!--!Fdrivers/char/drm/drm_irq.c drm_irq_install--> | |
| <para> | |
| Once your interrupt handler is registered (it'll use your | |
| drm_driver.irq_handler as the actual interrupt handling | |
| function), you can safely enable interrupts on your device, | |
| assuming any other state your interrupt handler uses is also | |
| initialized. | |
| </para> | |
| <para> | |
| Another task that may be necessary during configuration is | |
| mapping the video BIOS. On many devices, the VBIOS describes | |
| device configuration, LCD panel timings (if any), and contains | |
| flags indicating device state. Mapping the BIOS can be done | |
| using the pci_map_rom() call, a convenience function that | |
| takes care of mapping the actual ROM, whether it has been | |
| shadowed into memory (typically at address 0xc0000) or exists | |
| on the PCI device in the ROM BAR. Note that once you've | |
| mapped the ROM and extracted any necessary information, be | |
| sure to unmap it; on many devices the ROM address decoder is | |
| shared with other BARs, so leaving it mapped can cause | |
| undesired behavior like hangs or memory corruption. | |
| <!--!Fdrivers/pci/rom.c pci_map_rom--> | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>Memory manager initialization</title> | |
| <para> | |
| In order to allocate command buffers, cursor memory, scanout | |
| buffers, etc., as well as support the latest features provided | |
| by packages like Mesa and the X.Org X server, your driver | |
| should support a memory manager. | |
| </para> | |
| <para> | |
| If your driver supports memory management (it should!), you'll | |
| need to set that up at load time as well. How you initialize | |
| it depends on which memory manager you're using, TTM or GEM. | |
| </para> | |
| <sect3> | |
| <title>TTM initialization</title> | |
| <para> | |
| TTM (for Translation Table Manager) manages video memory and | |
| aperture space for graphics devices. TTM supports both UMA devices | |
| and devices with dedicated video RAM (VRAM), i.e. most discrete | |
| graphics devices. If your device has dedicated RAM, supporting | |
| TTM is desirable. TTM also integrates tightly with your | |
| driver specific buffer execution function. See the radeon | |
| driver for examples. | |
| </para> | |
| <para> | |
| The core TTM structure is the ttm_bo_driver struct. It contains | |
| several fields with function pointers for initializing the TTM, | |
| allocating and freeing memory, waiting for command completion | |
| and fence synchronization, and memory migration. See the | |
| radeon_ttm.c file for an example of usage. | |
| </para> | |
| <para> | |
| The ttm_global_reference structure is made up of several fields: | |
| </para> | |
| <programlisting> | |
| struct ttm_global_reference { | |
| enum ttm_global_types global_type; | |
| size_t size; | |
| void *object; | |
| int (*init) (struct ttm_global_reference *); | |
| void (*release) (struct ttm_global_reference *); | |
| }; | |
| </programlisting> | |
| <para> | |
| There should be one global reference structure for your memory | |
| manager as a whole, and there will be others for each object | |
| created by the memory manager at runtime. Your global TTM should | |
| have a type of TTM_GLOBAL_TTM_MEM. The size field for the global | |
| object should be sizeof(struct ttm_mem_global), and the init and | |
| release hooks should point at your driver specific init and | |
| release routines, which will probably eventually call | |
| ttm_mem_global_init and ttm_mem_global_release respectively. | |
| </para> | |
| <para> | |
| Once your global TTM accounting structure is set up and initialized | |
| (done by calling ttm_global_item_ref on the global object you | |
| just created), you'll need to create a buffer object TTM to | |
| provide a pool for buffer object allocation by clients and the | |
| kernel itself. The type of this object should be TTM_GLOBAL_TTM_BO, | |
| and its size should be sizeof(struct ttm_bo_global). Again, | |
| driver specific init and release functions can be provided, | |
| likely eventually calling ttm_bo_global_init and | |
| ttm_bo_global_release, respectively. Also like the previous | |
| object, ttm_global_item_ref is used to create an initial reference | |
| count for the TTM, which will call your initialization function. | |
| </para> | |
| </sect3> | |
| <sect3> | |
| <title>GEM initialization</title> | |
| <para> | |
| GEM is an alternative to TTM, designed specifically for UMA | |
| devices. It has simpler initialization and execution requirements | |
| than TTM, but has no VRAM management capability. Core GEM | |
| initialization is comprised of a basic drm_mm_init call to create | |
| a GTT DRM MM object, which provides an address space pool for | |
| object allocation. In a KMS configuration, the driver will | |
| need to allocate and initialize a command ring buffer following | |
| basic GEM initialization. Most UMA devices have a so-called | |
| "stolen" memory region, which provides space for the initial | |
| framebuffer and large, contiguous memory regions required by the | |
| device. This space is not typically managed by GEM, and must | |
| be initialized separately into its own DRM MM object. | |
| </para> | |
| <para> | |
| Initialization will be driver specific, and will depend on | |
| the architecture of the device. In the case of Intel | |
| integrated graphics chips like 965GM, GEM initialization can | |
| be done by calling the internal GEM init function, | |
| i915_gem_do_init(). Since the 965GM is a UMA device | |
| (i.e. it doesn't have dedicated VRAM), GEM will manage | |
| making regular RAM available for GPU operations. Memory set | |
| aside by the BIOS (called "stolen" memory by the i915 | |
| driver) will be managed by the DRM memrange allocator; the | |
| rest of the aperture will be managed by GEM. | |
| <programlisting> | |
| /* Basic memrange allocator for stolen space (aka vram) */ | |
| drm_memrange_init(&dev_priv->vram, 0, prealloc_size); | |
| /* Let GEM Manage from end of prealloc space to end of aperture */ | |
| i915_gem_do_init(dev, prealloc_size, agp_size); | |
| </programlisting> | |
| <!--!Edrivers/char/drm/drm_memrange.c--> | |
| </para> | |
| <para> | |
| Once the memory manager has been set up, we can allocate the | |
| command buffer. In the i915 case, this is also done with a | |
| GEM function, i915_gem_init_ringbuffer(). | |
| </para> | |
| </sect3> | |
| </sect2> | |
| <sect2> | |
| <title>Output configuration</title> | |
| <para> | |
| The final initialization task is output configuration. This involves | |
| finding and initializing the CRTCs, encoders and connectors | |
| for your device, creating an initial configuration and | |
| registering a framebuffer console driver. | |
| </para> | |
| <sect3> | |
| <title>Output discovery and initialization</title> | |
| <para> | |
| Several core functions exist to create CRTCs, encoders and | |
| connectors, namely drm_crtc_init(), drm_connector_init() and | |
| drm_encoder_init(), along with several "helper" functions to | |
| perform common tasks. | |
| </para> | |
| <para> | |
| Connectors should be registered with sysfs once they've been | |
| detected and initialized, using the | |
| drm_sysfs_connector_add() function. Likewise, when they're | |
| removed from the system, they should be destroyed with | |
| drm_sysfs_connector_remove(). | |
| </para> | |
| <programlisting> | |
| <![CDATA[ | |
| void intel_crt_init(struct drm_device *dev) | |
| { | |
| struct drm_connector *connector; | |
| struct intel_output *intel_output; | |
| intel_output = kzalloc(sizeof(struct intel_output), GFP_KERNEL); | |
| if (!intel_output) | |
| return; | |
| connector = &intel_output->base; | |
| drm_connector_init(dev, &intel_output->base, | |
| &intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA); | |
| drm_encoder_init(dev, &intel_output->enc, &intel_crt_enc_funcs, | |
| DRM_MODE_ENCODER_DAC); | |
| drm_mode_connector_attach_encoder(&intel_output->base, | |
| &intel_output->enc); | |
| /* Set up the DDC bus. */ | |
| intel_output->ddc_bus = intel_i2c_create(dev, GPIOA, "CRTDDC_A"); | |
| if (!intel_output->ddc_bus) { | |
| dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration " | |
| "failed.\n"); | |
| return; | |
| } | |
| intel_output->type = INTEL_OUTPUT_ANALOG; | |
| connector->interlace_allowed = 0; | |
| connector->doublescan_allowed = 0; | |
| drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs); | |
| drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); | |
| drm_sysfs_connector_add(connector); | |
| } | |
| ]]> | |
| </programlisting> | |
| <para> | |
| In the example above (again, taken from the i915 driver), a | |
| CRT connector and encoder combination is created. A device | |
| specific i2c bus is also created, for fetching EDID data and | |
| performing monitor detection. Once the process is complete, | |
| the new connector is registered with sysfs, to make its | |
| properties available to applications. | |
| </para> | |
| <sect4> | |
| <title>Helper functions and core functions</title> | |
| <para> | |
| Since many PC-class graphics devices have similar display output | |
| designs, the DRM provides a set of helper functions to make | |
| output management easier. The core helper routines handle | |
| encoder re-routing and disabling of unused functions following | |
| mode set. Using the helpers is optional, but recommended for | |
| devices with PC-style architectures (i.e. a set of display planes | |
| for feeding pixels to encoders which are in turn routed to | |
| connectors). Devices with more complex requirements needing | |
| finer grained management can opt to use the core callbacks | |
| directly. | |
| </para> | |
| <para> | |
| [Insert typical diagram here.] [Insert OMAP style config here.] | |
| </para> | |
| </sect4> | |
| <para> | |
| For each encoder, CRTC and connector, several functions must | |
| be provided, depending on the object type. Encoder objects | |
| need to provide a DPMS (basically on/off) function, mode fixup | |
| (for converting requested modes into native hardware timings), | |
| and prepare, set and commit functions for use by the core DRM | |
| helper functions. Connector helpers need to provide mode fetch and | |
| validity functions as well as an encoder matching function for | |
| returning an ideal encoder for a given connector. The core | |
| connector functions include a DPMS callback, (deprecated) | |
| save/restore routines, detection, mode probing, property handling, | |
| and cleanup functions. | |
| </para> | |
| <!--!Edrivers/char/drm/drm_crtc.h--> | |
| <!--!Edrivers/char/drm/drm_crtc.c--> | |
| <!--!Edrivers/char/drm/drm_crtc_helper.c--> | |
| </sect3> | |
| </sect2> | |
| </sect1> | |
| <!-- Internals: vblank handling --> | |
| <sect1> | |
| <title>VBlank event handling</title> | |
| <para> | |
| The DRM core exposes two vertical blank related ioctls: | |
| DRM_IOCTL_WAIT_VBLANK and DRM_IOCTL_MODESET_CTL. | |
| <!--!Edrivers/char/drm/drm_irq.c--> | |
| </para> | |
| <para> | |
| DRM_IOCTL_WAIT_VBLANK takes a struct drm_wait_vblank structure | |
| as its argument, and is used to block or request a signal when a | |
| specified vblank event occurs. | |
| </para> | |
| <para> | |
| DRM_IOCTL_MODESET_CTL should be called by application level | |
| drivers before and after mode setting, since on many devices the | |
| vertical blank counter will be reset at that time. Internally, | |
| the DRM snapshots the last vblank count when the ioctl is called | |
| with the _DRM_PRE_MODESET command so that the counter won't go | |
| backwards (which is dealt with when _DRM_POST_MODESET is used). | |
| </para> | |
| <para> | |
| To support the functions above, the DRM core provides several | |
| helper functions for tracking vertical blank counters, and | |
| requires drivers to provide several callbacks: | |
| get_vblank_counter(), enable_vblank() and disable_vblank(). The | |
| core uses get_vblank_counter() to keep the counter accurate | |
| across interrupt disable periods. It should return the current | |
| vertical blank event count, which is often tracked in a device | |
| register. The enable and disable vblank callbacks should enable | |
| and disable vertical blank interrupts, respectively. In the | |
| absence of DRM clients waiting on vblank events, the core DRM | |
| code will use the disable_vblank() function to disable | |
| interrupts, which saves power. They'll be re-enabled again when | |
| a client calls the vblank wait ioctl above. | |
| </para> | |
| <para> | |
| Devices that don't provide a count register can simply use an | |
| internal atomic counter incremented on every vertical blank | |
| interrupt, and can make their enable and disable vblank | |
| functions into no-ops. | |
| </para> | |
| </sect1> | |
| <sect1> | |
| <title>Memory management</title> | |
| <para> | |
| The memory manager lies at the heart of many DRM operations, and | |
| is also required to support advanced client features like OpenGL | |
| pbuffers. The DRM currently contains two memory managers, TTM | |
| and GEM. | |
| </para> | |
| <sect2> | |
| <title>The Translation Table Manager (TTM)</title> | |
| <para> | |
| TTM was developed by Tungsten Graphics, primarily by Thomas | |
| Hellström, and is intended to be a flexible, high performance | |
| graphics memory manager. | |
| </para> | |
| <para> | |
| Drivers wishing to support TTM must fill out a drm_bo_driver | |
| structure. | |
| </para> | |
| <para> | |
| TTM design background and information belongs here. | |
| </para> | |
| </sect2> | |
| <sect2> | |
| <title>The Graphics Execution Manager (GEM)</title> | |
| <para> | |
| GEM is an Intel project, authored by Eric Anholt and Keith | |
| Packard. It provides simpler interfaces than TTM, and is well | |
| suited for UMA devices. | |
| </para> | |
| <para> | |
| GEM-enabled drivers must provide gem_init_object() and | |
| gem_free_object() callbacks to support the core memory | |
| allocation routines. They should also provide several driver | |
| specific ioctls to support command execution, pinning, buffer | |
| read & write, mapping, and domain ownership transfers. | |
| </para> | |
| <para> | |
| On a fundamental level, GEM involves several operations: memory | |
| allocation and freeing, command execution, and aperture management | |
| at command execution time. Buffer object allocation is relatively | |
| straightforward and largely provided by Linux's shmem layer, which | |
| provides memory to back each object. When mapped into the GTT | |
| or used in a command buffer, the backing pages for an object are | |
| flushed to memory and marked write combined so as to be coherent | |
| with the GPU. Likewise, when the GPU finishes rendering to an object, | |
| if the CPU accesses it, it must be made coherent with the CPU's view | |
| of memory, usually involving GPU cache flushing of various kinds. | |
| This core CPU<->GPU coherency management is provided by the GEM | |
| set domain function, which evaluates an object's current domain and | |
| performs any necessary flushing or synchronization to put the object | |
| into the desired coherency domain (note that the object may be busy, | |
| i.e. an active render target; in that case the set domain function | |
| will block the client and wait for rendering to complete before | |
| performing any necessary flushing operations). | |
| </para> | |
| <para> | |
| Perhaps the most important GEM function is providing a command | |
| execution interface to clients. Client programs construct command | |
| buffers containing references to previously allocated memory objects | |
| and submit them to GEM. At that point, GEM will take care to bind | |
| all the objects into the GTT, execute the buffer, and provide | |
| necessary synchronization between clients accessing the same buffers. | |
| This often involves evicting some objects from the GTT and re-binding | |
| others (a fairly expensive operation), and providing relocation | |
| support which hides fixed GTT offsets from clients. Clients must | |
| take care not to submit command buffers that reference more objects | |
| than can fit in the GTT or GEM will reject them and no rendering | |
| will occur. Similarly, if several objects in the buffer require | |
| fence registers to be allocated for correct rendering (e.g. 2D blits | |
| on pre-965 chips), care must be taken not to require more fence | |
| registers than are available to the client. Such resource management | |
| should be abstracted from the client in libdrm. | |
| </para> | |
| </sect2> | |
| </sect1> | |
| <!-- Output management --> | |
| <sect1> | |
| <title>Output management</title> | |
| <para> | |
| At the core of the DRM output management code is a set of | |
| structures representing CRTCs, encoders and connectors. | |
| </para> | |
| <para> | |
| A CRTC is an abstraction representing a part of the chip that | |
| contains a pointer to a scanout buffer. Therefore, the number | |
| of CRTCs available determines how many independent scanout | |
| buffers can be active at any given time. The CRTC structure | |
| contains several fields to support this: a pointer to some video | |
| memory, 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. | |
| </para> | |
| <para> | |
| 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. | |
| </para> | |
| <para> | |
| 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. | |
| </para> | |
| <!--!Edrivers/char/drm/drm_crtc.c--> | |
| </sect1> | |
| <sect1> | |
| <title>Framebuffer management</title> | |
| <para> | |
| In order to set a mode on a given CRTC, encoder and connector | |
| configuration, clients need to provide a framebuffer object which | |
| will provide a source of pixels for the CRTC to deliver to the encoder(s) | |
| and ultimately the connector(s) in the configuration. A framebuffer | |
| is fundamentally a driver specific memory object, made into an opaque | |
| handle by the DRM addfb function. Once an fb has been created this | |
| way it can be passed to the KMS mode setting routines for use in | |
| a configuration. | |
| </para> | |
| </sect1> | |
| <sect1> | |
| <title>Command submission & fencing</title> | |
| <para> | |
| This should cover a few device specific command submission | |
| implementations. | |
| </para> | |
| </sect1> | |
| <sect1> | |
| <title>Suspend/resume</title> | |
| <para> | |
| The DRM core provides some suspend/resume code, but drivers | |
| wanting full suspend/resume support should provide save() and | |
| restore() functions. These will be called at suspend, | |
| hibernate, or resume time, and should perform any state save or | |
| restore required by your device across suspend or hibernate | |
| states. | |
| </para> | |
| </sect1> | |
| <sect1> | |
| <title>DMA services</title> | |
| <para> | |
| This should cover how DMA mapping etc. is supported by the core. | |
| These functions are deprecated and should not be used. | |
| </para> | |
| </sect1> | |
| </chapter> | |
| <!-- External interfaces --> | |
| <chapter id="drmExternals"> | |
| <title>Userland interfaces</title> | |
| <para> | |
| The DRM core exports several interfaces to applications, | |
| generally intended to be used through corresponding libdrm | |
| wrapper functions. In addition, drivers export device specific | |
| interfaces for use by userspace drivers & device aware | |
| applications through ioctls and sysfs files. | |
| </para> | |
| <para> | |
| External interfaces include: memory mapping, context management, | |
| DMA operations, AGP management, vblank control, fence | |
| management, memory management, and output management. | |
| </para> | |
| <para> | |
| Cover generic ioctls and sysfs layout here. Only need high | |
| level info, since man pages will cover the rest. | |
| </para> | |
| </chapter> | |
| <!-- API reference --> | |
| <appendix id="drmDriverApi"> | |
| <title>DRM Driver API</title> | |
| <para> | |
| Include auto-generated API reference here (need to reference it | |
| from paragraphs above too). | |
| </para> | |
| </appendix> | |
| </book> |