mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-25 04:40:53 +07:00
dc880abef7
The drm device drivers currently allow seeking on the character device but never care about the actual file position. When we change the default llseek operation to be no_llseek, calling llseek on a drm device would return an error condition, which is an API change. Explicitly setting noop_llseek lets us keep the current API. Signed-off-by: Arnd Bergmann <arnd@arndb.de> Cc: David Airlie <airlied@linux.ie> Cc: dri-devel@lists.freedesktop.org
841 lines
32 KiB
XML
841 lines
32 KiB
XML
<?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_device. Drivers
|
|
will typically statically initialize a drm_device 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_device 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>
|