Skip to content

Cyclic DMA

Cyclic DMA is a DMA Engine transfer mode designed for continuous data streams that repeatedly traverse the same DMA buffer.

Unlike a one-shot DMA transaction, a cyclic transaction does not normally finish after reaching the end of the buffer. The DMA controller wraps back to the beginning and continues transferring data until the client stops the transaction.

Typical use cases include continuous audio, serial data streams, and other producer/consumer workloads that repeatedly reuse a fixed DMA buffer.


Mental Model

A cyclic DMA buffer is divided into multiple periods.

For example:

Cyclic Buffer

┌──────────────┬──────────────┬──────────────┬──────────────┐
│   Period 0   │   Period 1   │   Period 2   │   Period 3   │
└──────────────┴──────────────┴──────────────┴──────────────┘
       ↑                                             │
       └─────────────────────────────────────────────┘
                         wrap

The entire buffer belongs to one long-running DMA transaction.

The individual periods are not separate DMA transactions.

One Descriptor
One Cookie
One Long-Running Transaction

P0 → P1 → P2 → P3
↑              │
└──────────────┘

The client prepares and submits the cyclic descriptor once. The DMA controller then repeatedly traverses the buffer until the transaction is terminated.


Cyclic DMA vs Repeated One-Shot DMA

Cyclic DMA should not be modeled as repeatedly completing and resubmitting the same descriptor.

Incorrect model:

Transfer Period
Complete Descriptor
Reuse Descriptor
Submit Again
Transfer Next Period

Cyclic DMA instead keeps the same long-running transaction active:

Submit Once
Issue
P0 → P1 → P2 → P3
↑              │
└──────────────┘

Therefore:

Cyclic wrap
Descriptor completion
      +
Descriptor reuse
      +
Resubmission

The mechanism that implements the wrap is provided by the DMA controller driver and hardware.


Preparing a Cyclic Transaction

DMA clients prepare cyclic transfers using:

struct dma_async_tx_descriptor *
dmaengine_prep_dma_cyclic(struct dma_chan *chan,
                          dma_addr_t buf_addr,
                          size_t buf_len,
                          size_t period_len,
                          enum dma_transfer_direction dir,
                          unsigned long flags);

The important buffer parameters are:

Parameter Description
buf_addr DMA address of the cyclic buffer.
buf_len Total length of the cyclic buffer.
period_len Length of each notification period.
dir DMA transfer direction.
flags DMA descriptor preparation flags.

For example:

buf_len    = 4096
period_len = 1024

┌──────────┬──────────┬──────────┬──────────┐
│    P0    │    P1    │    P2    │    P3    │
└──────────┴──────────┴──────────┴──────────┘
   1024       1024       1024       1024

In this example, one cyclic transaction contains four periods.

The exact hardware representation is controller-specific.


DMA Engine and Controller Responsibilities

The DMA Engine framework provides a generic cyclic DMA interface.

DMA Client
dmaengine_prep_dma_cyclic()
DMA Engine Framework
device_prep_dma_cyclic()
DMA Controller Driver
DMA Hardware

The controller driver translates the generic request into whatever mechanism its DMA controller supports.

One controller might build a hardware descriptor ring:

Descriptor 0
Descriptor 1
Descriptor 2
Descriptor 3
     └──────────→ Descriptor 0

Another controller may provide native circular-buffer or cyclic hardware support.

DMA Engine therefore defines the cyclic transaction semantics, but does not require every controller to implement cyclic execution in the same way.


Periods

A period is a progress and notification unit within the cyclic buffer.

It is not an independent DMA transaction.

             One Cyclic Transaction
┌─────────────────────────────────────────────┐
│                                             │
│  P0 → P1 → P2 → P3 → P0 → P1 → ...          │
│                                             │
└─────────────────────────────────────────────┘

The periods do not receive independent DMA cookies and are not separately submitted through dmaengine_submit().

Therefore:

One Period
One DMA Transaction

Period Callbacks

A client can associate a callback with the cyclic descriptor before submission.

Conceptually:

desc = dmaengine_prep_dma_cyclic(chan,
                                 dma_addr,
                                 buf_len,
                                 period_len,
                                 direction,
                                 flags);
if (!desc)
    return -EIO;

desc->callback = period_callback;
desc->callback_param = context;

cookie = dmaengine_submit(desc);
if (dma_submit_error(cookie))
    return dma_submit_error(cookie);

dma_async_issue_pending(chan);

The same callback can then be invoked repeatedly as DMA crosses period boundaries.

P0 → P1 → P2 → P3 → P0 → ...
│    │    │    │    │
cb   cb   cb   cb   cb

The callback reports progress inside the cyclic transaction.

It does not normally indicate that the entire transaction has completed.


Period Completion vs Transaction Completion

This distinction is fundamental to cyclic DMA.

For a normal one-shot transaction:

DMA Transaction Finished
Cookie Completion
Completion Processing
Client Callback

For a cyclic transaction:

Period Boundary
Period Callback
Same Transaction Continues

Therefore:

Period Callback
Transaction Completion

A period boundary does not normally retire the cyclic descriptor.


The cyclic descriptor receives one cookie when it is submitted.

For example:

cookie = 42

P0 → callback
P1 → callback
P2 → callback
P3 → callback
P0 → callback
...

These callbacks still belong to the same transaction and descriptor.

The cookie is not replaced for every period.

Therefore:

Period Completion
dma_cookie_complete()

Cookie completion bookkeeping should not be interpreted as a per-period counter.


virt-dma Cyclic Callback Path

The virt-dma infrastructure explicitly distinguishes cyclic period callbacks from normal descriptor completion.

A virt-dma-based controller may report a cyclic period event using:

vchan_cyclic_callback(vd);

Normal descriptor completion instead uses:

vchan_cookie_complete(vd);

Conceptually:

Controller Event
Is Descriptor Cyclic?
       ├── Yes
       │     ↓
       │  vchan_cyclic_callback()
       │     ↓
       │  Period Callback
       │     ↓
       │  Descriptor Remains Active
       └── No
         vchan_cookie_complete()
         Normal Transaction Completion

The important distinction is:

vchan_cyclic_callback()
vchan_cookie_complete()

Although cyclic callback processing can reach the virt-dma deferred processing function named vchan_complete(), this does not mean the cyclic transaction has completed.

vchan_complete() participates in both normal completion processing and cyclic callback processing.


DMA and Callback Execution

DMA hardware does not normally need to wait for the client callback before continuing into the next period.

For an RX stream:

CPU                         DMA

                            Fill P0
                         P0 Boundary
P0 Callback  ←──────── notification
    │                          │
Process P0                  Fill P1
    │                          │
    ▼                          ▼

There can be latency between:

Period Boundary
IRQ Handling
Deferred Processing
Client Callback

Therefore, by the time the callback executes, DMA hardware may already have progressed into the next period.

A period callback indicates that a boundary occurred. It does not imply that DMA hardware is currently stopped at that boundary.


Cyclic DMA Lifecycle

The complete cyclic DMA lifecycle is shown below.

Cyclic DMA lifecycle

The important property is that period callbacks occur inside the lifetime of the same cyclic descriptor and transaction.

The transaction continues until the client begins the stop sequence.


Producer and Consumer Model

Cyclic DMA naturally creates a producer/consumer relationship between DMA hardware and software.

Peripheral to Memory

For DMA_DEV_TO_MEM:

Peripheral
DMA
Cyclic Buffer

The roles can be modeled as:

Role Component
Producer DMA hardware
Consumer CPU / DMA client

After DMA completes one period and advances to the next, software can process the completed region according to the client or subsystem protocol.


Memory to Peripheral

For DMA_MEM_TO_DEV:

Cyclic Buffer
DMA
Peripheral

The roles are reversed:

Role Component
Producer CPU / DMA client
Consumer DMA hardware

After DMA consumes a region, software can prepare it for future reuse according to the client or subsystem protocol.


Buffer Processing Window

Consider a four-period RX buffer:

P0 → P1 → P2 → P3 → P0
↑                   │
└───────────────────┘

After P0 completes, software has the time during which DMA traverses the remaining periods before DMA eventually returns to P0.

Conceptually:

CPU:
Process P0
└─────────────────────────────┐
DMA:                          │
P1 → P2 → P3 ─────────────────┘
                         Return to P0

The number and size of periods therefore affect the buffering window between producer and consumer.


Overrun and Underrun

Cyclic DMA does not provide unlimited buffering.

For RX, DMA may eventually return to a region that software has not finished consuming:

DMA:

P0 → P1 → P2 → P3 → P0
              overwrite old data

For TX, DMA may reach a region that software has not prepared in time.

These conditions can result in overrun, underrun, stale data, or other subsystem-specific behavior.

DMA Engine does not automatically solve this producer/consumer synchronization problem.

The client and subsystem must ensure that software keeps up with the required data rate.


Period Callbacks Are Progress Notifications

Period callbacks should not be treated as unlimited historical event storage.

A safer model is:

DMA Progress
Period Boundary
Period Notification
Update / Wake Software Processing

Even if software records callback history, the cyclic buffer itself continues to be reused.

Old data may already have been overwritten when DMA wraps.

Therefore:

Callback backlog does not imply that historical buffer contents are still available.


DMA Residue and Position

DMA clients can query transaction state using:

dmaengine_tx_status()

The returned struct dma_tx_state can contain residue information.

For controllers that provide suitable cyclic residue reporting, software may be able to infer the current position inside the buffer.

A common conceptual relationship is:

position = buf_len - residue

However, residue reporting is controller-dependent.

The DMA controller's residue granularity determines how accurately progress can be reported.

Therefore, generic client code must not assume that residue always provides a byte-accurate hardware position.

It is useful to keep three concepts separate:

Mechanism Meaning
Cookie Transaction lifecycle and status
Residue Transfer progress or position information
Period callback Period progress notification

Stopping a Cyclic Transaction

A cyclic transaction normally continues until the client explicitly stops it.

Typical stop conditions include:

  • A requested number of periods has been processed.
  • A requested streaming duration has elapsed.
  • Userspace requests the stream to stop.
  • Device shutdown or removal begins.
  • An error requires the stream to stop.

A client-defined finite duration does not prevent cyclic DMA from being appropriate.

For example:

Start Cyclic DMA
Continuous Streaming
100 Periods Reached
Stop

or:

Start Cyclic DMA
Continuous Streaming
10 Seconds Elapsed
Stop

The important property is that DMA remains continuous while the stream is active.


Cyclic DMA vs Periodically Triggered DMA

Cyclic DMA should be distinguished from periodically starting independent DMA transactions.

Cyclic streaming:

Start
P0 → P1 → P2 → P3 → P0 → ...
Stop

Periodically triggered one-shot DMA:

Transfer
Idle N Seconds
Transfer
Idle N Seconds
Transfer

The second pattern is generally better modeled using a timer, delayed work, thread, or another scheduling mechanism that periodically starts a one-shot DMA transaction.

A useful decision question is:

Should DMA continuously stream between two samples, or should DMA actually be idle?

Continuous streaming with repeated buffer traversal is a strong cyclic DMA use case.

Long idle intervals between independent transfers usually are not.


Termination and Synchronization

The standard DMA Engine termination model applies to cyclic DMA.

A safe client-side shutdown sequence is:

RUNNING
STOPPING
Prevent New Client Work
dmaengine_terminate_async()
dmaengine_synchronize()
Safe Cleanup
STOPPED

When synchronous waiting is allowed, the client may use:

dmaengine_terminate_sync(chan);

instead.

Termination should not be confused with normal transaction completion.

Normal Completion
Termination

The exact controller-side handling of the active cyclic descriptor and its cookie is provider-specific.


Callback Lifetime During Shutdown

Stopping DMA hardware does not automatically prove that all previously scheduled period callback activity has finished.

A race can look like:

Period Boundary
Callback Pending
      │       Client STOPPING
      │              ↓
      │       Terminate DMA
Deferred Callback

Therefore:

DMA Hardware Stopped
Callback Quiescent

Synchronization establishes the boundary after which the client can safely release resources that DMA-related asynchronous activity might reference.

This includes:

  • Callback context.
  • DMA buffer metadata.
  • Driver state referenced by callbacks.
  • Other client resources tied to the DMA stream.

Typical Client Workflow

A complete cyclic DMA client workflow can be modeled as:

Allocate / Map DMA Buffer
Request and Configure DMA Channel
dmaengine_prep_dma_cyclic()
Set Callback and Callback Parameter
dmaengine_submit()
Cookie Assigned
dma_async_issue_pending()
┌─────────────────────────────┐
│      CYCLIC RUNNING         │
│                             │
│ P0 → callback               │
│ P1 → callback               │
│ P2 → callback               │
│ P3 → callback               │
│ P0 → callback               │
│ ...                         │
└─────────────────────────────┘
Stop Condition
Prevent New Client Work
Terminate
Synchronize
Release Resources

The callback context and DMA-related resources must remain valid for the required lifetime of the cyclic transaction and any asynchronous callback activity.


Key Takeaways

  • Cyclic DMA is one long-running DMA transaction.
  • A cyclic descriptor is prepared and submitted once.
  • Periods are notification units, not independent DMA transactions.
  • Period callbacks do not normally complete the cyclic transaction.
  • The same descriptor and cookie remain associated with multiple period callbacks.
  • vchan_cyclic_callback() is different from normal vchan_cookie_complete().
  • DMA hardware can continue transferring while software processes a period callback.
  • Cyclic DMA creates a producer/consumer relationship between DMA and software.
  • Software must keep up with the stream before the DMA buffer wraps.
  • Residue can provide progress information, but its precision is controller-dependent.
  • Finite count or duration can still be valid cyclic DMA use cases.
  • Periodically triggered one-shot DMA is different from cyclic DMA.
  • Cyclic DMA normally ends through explicit termination.
  • Termination does not by itself guarantee callback quiescence.
  • Synchronization is required before releasing resources that asynchronous DMA activity may still reference.