drm/amd/display - Display Core (DC)

placeholder - general description of supported platforms, what dc is, etc.

Because it is partially shared with other operating systems, the Display Core Driver is divided in two pieces.

  1. Display Core (DC) contains the OS-agnostic components. Things like hardware programming and resource management are handled here.

  2. Display Manager (DM) contains the OS-dependent components. Hooks to the amdgpu base driver and DRM are implemented here.

It doesn't help that the entire package is frequently referred to as DC. But with the context in mind, it should be clear.

When CONFIG_DRM_AMD_DC is enabled, DC will be initialized by default for supported ASICs. To force disable, set amdgpu.dc=0 on kernel command line. Likewise, to force enable on unsupported ASICs, set amdgpu.dc=1.

To determine if DC is loaded, search dmesg for the following entry:

Display Core initialized with <version number here>

AMDgpu Display Manager

The AMDgpu display manager, amdgpu_dm (or even simpler, dm) sits between DRM and DC. It acts as a liason, converting DRM requests into DC requests, and DC responses into DRM responses.

The root control structure is struct amdgpu_display_manager.

struct irq_list_head

Linked-list for low context IRQ handlers.

Definition

struct irq_list_head {
  struct list_head head;
  struct work_struct work;
};

Members

head

The list_head within struct handler_data

work

A work_struct containing the deferred handler work

struct dm_comressor_info

Buffer info used by frame buffer compression

Definition

struct dm_comressor_info {
  void *cpu_addr;
  struct amdgpu_bo *bo_ptr;
  uint64_t gpu_addr;
};

Members

cpu_addr

MMIO cpu addr

bo_ptr

Pointer to the buffer object

gpu_addr

MMIO gpu addr

struct amdgpu_dm_backlight_caps

Usable range of backlight values from ACPI

Definition

struct amdgpu_dm_backlight_caps {
  int min_input_signal;
  int max_input_signal;
  bool caps_valid;
};

Members

min_input_signal

minimum possible input in range 0-255

max_input_signal

maximum possible input in range 0-255

caps_valid

true if these values are from the ACPI interface

struct amdgpu_display_manager

Central amdgpu display manager device

Definition

struct amdgpu_display_manager {
  struct dc *dc;
  struct cgs_device *cgs_device;
  struct amdgpu_device *adev;
  struct drm_device *ddev;
  u16 display_indexes_num;
  struct drm_private_obj atomic_obj;
  struct mutex dc_lock;
  struct irq_list_head irq_handler_list_low_tab[DAL_IRQ_SOURCES_NUMBER];
  struct list_head irq_handler_list_high_tab[DAL_IRQ_SOURCES_NUMBER];
  struct common_irq_params pflip_params[DC_IRQ_SOURCE_PFLIP_LAST - DC_IRQ_SOURCE_PFLIP_FIRST + 1];
  struct common_irq_params vblank_params[DC_IRQ_SOURCE_VBLANK6 - DC_IRQ_SOURCE_VBLANK1 + 1];
  struct common_irq_params vupdate_params[DC_IRQ_SOURCE_VUPDATE6 - DC_IRQ_SOURCE_VUPDATE1 + 1];
  spinlock_t irq_handler_list_table_lock;
  struct backlight_device *backlight_dev;
  const struct dc_link *backlight_link;
  struct amdgpu_dm_backlight_caps backlight_caps;
  struct mod_freesync *freesync_module;
  struct drm_atomic_state *cached_state;
  struct dm_comressor_info compressor;
  const struct firmware *fw_dmcu;
  uint32_t dmcu_fw_version;
};

Members

dc

Display Core control structure

cgs_device

The Common Graphics Services device. It provides an interface for accessing registers.

adev

AMDGPU base driver structure

ddev

DRM base driver structure

display_indexes_num

Max number of display streams supported

dc_lock

Guards access to DC functions that can issue register write sequences.

irq_handler_list_low_tab

Low priority IRQ handler table.

It is a n*m table consisting of n IRQ sources, and m handlers per IRQ source. Low priority IRQ handlers are deferred to a workqueue to be processed. Hence, they can sleep.

Note that handlers are called in the same order as they were registered (FIFO).

irq_handler_list_high_tab

High priority IRQ handler table.

It is a n*m table, same as irq_handler_list_low_tab. However, handlers in this table are not deferred and are called immediately.

pflip_params

Page flip IRQ parameters, passed to registered handlers when triggered.

vblank_params

Vertical blanking IRQ parameters, passed to registered handlers when triggered.

vupdate_params

Vertical update IRQ parameters, passed to registered handlers when triggered.

irq_handler_list_table_lock

Synchronizes access to IRQ tables

backlight_dev

Backlight control device

cached_state

Caches device atomic state for suspend/resume

compressor

Frame buffer compression buffer. See struct dm_comressor_info

Lifecycle

DM (and consequently DC) is registered in the amdgpu base driver as a IP block. When CONFIG_DRM_AMD_DC is enabled, the DM device IP block is added to the base driver's device list to be initialized and torn down accordingly.

The functions to do so are provided as hooks in struct amd_ip_funcs.

int dm_hw_init(void * handle)

Initialize DC device

Parameters

void * handle

The base driver device containing the amdpgu_dm device.

Description

Initialize the struct amdgpu_display_manager device. This involves calling the initializers of each DM component, then populating the struct with them.

Although the function implies hardware initialization, both hardware and software are initialized here. Splitting them out to their relevant init hooks is a future TODO item.

Some notable things that are initialized here:

  • Display Core, both software and hardware

  • DC modules that we need (freesync and color management)

  • DRM software states

  • Interrupt sources and handlers

  • Vblank support

  • Debug FS entries, if enabled

int dm_hw_fini(void * handle)

Teardown DC device

Parameters

void * handle

The base driver device containing the amdpgu_dm device.

Description

Teardown components within struct amdgpu_display_manager that require cleanup. This involves cleaning up the DRM device, DC, and any modules that were loaded. Also flush IRQ workqueues and disable them.

Interrupts

DM provides another layer of IRQ management on top of what the base driver already provides. This is something that could be cleaned up, and is a future TODO item.

The base driver provides IRQ source registration with DRM, handler registration into the base driver's IRQ table, and a handler callback amdgpu_irq_handler(), with which DRM calls on interrupts. This generic handler looks up the IRQ table, and calls the respective amdgpu_irq_src_funcs.process hookups.

What DM provides on top are two IRQ tables specifically for top-half and bottom-half IRQ handling, with the bottom-half implementing workqueues:

They override the base driver's IRQ table, and the effect can be seen in the hooks that DM provides for amdgpu_irq_src_funcs.process. They are all set to the DM generic handler amdgpu_dm_irq_handler(), which looks up DM's IRQ tables. However, in order for base driver to recognize this hook, DM still needs to register the IRQ with the base driver. See dce110_register_irq_handlers() and dcn10_register_irq_handlers().

To expose DC's hardware interrupt toggle to the base driver, DM implements amdgpu_irq_src_funcs.set hooks. Base driver calls it through amdgpu_irq_update() to enable or disable the interrupt.

struct amdgpu_dm_irq_handler_data

Data for DM interrupt handlers.

Definition

struct amdgpu_dm_irq_handler_data {
  struct list_head list;
  interrupt_handler handler;
  void *handler_arg;
  struct amdgpu_display_manager *dm;
  enum dc_irq_source irq_source;
};

Members

list

Linked list entry referencing the next/previous handler

handler

Handler function

handler_arg

Argument passed to the handler when triggered

dm

DM which this handler belongs to

irq_source

DC interrupt source that this handler is registered for

void dm_irq_work_func(struct work_struct * work)

Handle an IRQ outside of the interrupt handler proper.

Parameters

struct work_struct * work

work struct

void * amdgpu_dm_irq_register_interrupt(struct amdgpu_device * adev, struct dc_interrupt_params * int_params, void (*ih) (void *, void * handler_args)

Register a handler within DM.

Parameters

struct amdgpu_device * adev

The base driver device containing the DM device.

struct dc_interrupt_params * int_params

Interrupt parameters containing the source, and handler context

void (*)(void *) ih

Function pointer to the interrupt handler to register

void * handler_args

Arguments passed to the handler when the interrupt occurs

Description

Register an interrupt handler for the given IRQ source, under the given context. The context can either be high or low. High context handlers are executed directly within ISR context, while low context is executed within a workqueue, thereby allowing operations that sleep.

Registered handlers are called in a FIFO manner, i.e. the most recently registered handler will be called first.

Return

Handler data struct amdgpu_dm_irq_handler_data containing the IRQ

source, handler function, and args

void amdgpu_dm_irq_unregister_interrupt(struct amdgpu_device * adev, enum dc_irq_source irq_source, void * ih)

Remove a handler from the DM IRQ table

Parameters

struct amdgpu_device * adev

The base driver device containing the DM device

enum dc_irq_source irq_source

IRQ source to remove the given handler from

void * ih

Function pointer to the interrupt handler to unregister

Description

Go through both low and high context IRQ tables, and find the given handler for the given irq source. If found, remove it. Otherwise, do nothing.

int amdgpu_dm_irq_init(struct amdgpu_device * adev)

Initialize DM IRQ management

Parameters

struct amdgpu_device * adev

The base driver device containing the DM device

Description

Initialize DM's high and low context IRQ tables.

The N by M table contains N IRQ sources, with M struct amdgpu_dm_irq_handler_data hooked together in a linked list. The list_heads are initialized here. When an interrupt n is triggered, all m handlers are called in sequence, FIFO according to registration order.

The low context table requires special steps to initialize, since handlers will be deferred to a workqueue. See struct irq_list_head.

void amdgpu_dm_irq_fini(struct amdgpu_device * adev)

Tear down DM IRQ management

Parameters

struct amdgpu_device * adev

The base driver device containing the DM device

Description

Flush all work within the low context IRQ table.

int amdgpu_dm_irq_handler(struct amdgpu_device * adev, struct amdgpu_irq_src * source, struct amdgpu_iv_entry * entry)

Generic DM IRQ handler

Parameters

struct amdgpu_device * adev

amdgpu base driver device containing the DM device

struct amdgpu_irq_src * source

Unused

struct amdgpu_iv_entry * entry

Data about the triggered interrupt

Description

Calls all registered high irq work immediately, and schedules work for low irq. The DM IRQ table is used to find the corresponding handlers.

void amdgpu_dm_hpd_init(struct amdgpu_device * adev)

hpd setup callback.

Parameters

struct amdgpu_device * adev

amdgpu_device pointer

Description

Setup the hpd pins used by the card (evergreen+). Enable the pin, set the polarity, and enable the hpd interrupts.

void amdgpu_dm_hpd_fini(struct amdgpu_device * adev)

hpd tear down callback.

Parameters

struct amdgpu_device * adev

amdgpu_device pointer

Description

Tear down the hpd pins used by the card (evergreen+). Disable the hpd interrupts.

Atomic Implementation

WIP

void amdgpu_dm_atomic_commit_tail(struct drm_atomic_state * state)

AMDgpu DM's commit tail implementation.

Parameters

struct drm_atomic_state * state

The atomic state to commit

Description

This will tell DC to commit the constructed DC state from atomic_check, programming the hardware. Any failures here implies a hardware failure, since atomic check should have filtered anything non-kosher.

int amdgpu_dm_atomic_check(struct drm_device * dev, struct drm_atomic_state * state)

Atomic check implementation for AMDgpu DM.

Parameters

struct drm_device * dev

The DRM device

struct drm_atomic_state * state

The atomic state to commit

Description

Validate that the given atomic state is programmable by DC into hardware. This involves constructing a struct dc_state reflecting the new hardware state we wish to commit, then querying DC to see if it is programmable. It's important not to modify the existing DC state. Otherwise, atomic_check may unexpectedly commit hardware changes.

When validating the DC state, it's important that the right locks are acquired. For full updates case which removes/adds/updates streams on one CRTC while flipping on another CRTC, acquiring global lock will guarantee that any such full update commit will wait for completion of any outstanding flip using DRMs synchronization events. See dm_determine_update_type_for_commit()

Note that DM adds the affected connectors for all CRTCs in state, when that might not seem necessary. This is because DC stream creation requires the DC sink, which is tied to the DRM connector state. Cleaning this up should be possible but non-trivial - a possible TODO item.

Return

-Error code if validation failed.

Display Core

WIP