PXA/MMP - DMA 从设备控制器

约束

a) 传输热队列:驱动程序提交传输并发出它,即使在运行的 DMA 通道上,也应保证该传输已排队。 这意味着排队不会等待上一次传输结束,并且描述符链接不仅在传输结束触发的 irq/tasklet 代码中完成。 在 phy 上提交和发出的传输不会等待 phy 停止并重新启动,而是在“运行通道”上提交。 其他驱动程序,尤其是 mmp_pdma,在重新启动新的传输之前会等待 phy 停止。

b) 所有要求确认的传输都应发出信号:任何带有 DMA_PREP_INTERRUPT 的已发出传输都应触发回调调用。 这意味着即使 irq/tasklet 是由 tx1 结束触发的,但在 irq/dma 时 tx2 已经完成,也应该调用 tx1->complete() 和 tx2->complete()。

c) 通道运行状态:驱动程序应该能够查询通道是否正在运行。 对于多媒体情况,例如视频捕获,如果提交了一个传输,然后对 DMA 通道的检查报告“通道已停止”,则在下一个“帧启动中断”之前不应发出该传输,因此需要知道通道处于运行状态还是停止状态。

d) 带宽保证:PXA 架构有 4 个 DMA 优先级:高、正常、低。 高优先级获得的带宽是正常的两倍,正常优先级获得的带宽是低优先级的两倍。 驱动程序应该能够请求优先级,特别是像 pxa_camera 这样具有(大)吞吐量的实时驱动程序。

设计

a) 虚拟通道:与 sa11x0 驱动程序中的概念相同,即驱动程序被分配了一个链接到请求者线的“虚拟通道”,并且物理 DMA 通道在发出传输时动态分配。

  1. 用于散布-收集传输的传输解剖

+------------+-----+---------------+----------------+-----------------+
| desc-sg[0] | ... | desc-sg[last] | status updater | finisher/linker |
+------------+-----+---------------+----------------+-----------------+

此结构由 dma->sg_cpu 指向。 描述符的使用方式如下

  • desc-sg[i]:第 i 个描述符,将第 i 个 sg 元素传输到视频缓冲区散布收集

  • 状态更新器:将单个 u32 传输到众所周知的 dma 一致性内存,以留下此传输已完成的痕迹。 “众所周知”对于每个物理通道都是唯一的,这意味着读取此值将告诉您此时完成的最后一个传输。

  • finisher: 具有 ddadr=DADDR_STOP, dcmd=ENDIRQEN

  • linker: 具有 ddadr= 下一次传输的 desc-sg[0], dcmd=0

c) 传输热链:假设运行链是

Buffer 1              Buffer 2
+---------+----+---+  +----+----+----+---+
| d0 | .. | dN | l |  | d0 | .. | dN | f |
+---------+----+-|-+  ^----+----+----+---+
                 |    |
                 +----+

在调用 dmaengine_submit(b3) 之后,链将如下所示

Buffer 1              Buffer 2              Buffer 3
+---------+----+---+  +----+----+----+---+  +----+----+----+---+
| d0 | .. | dN | l |  | d0 | .. | dN | l |  | d0 | .. | dN | f |
+---------+----+-|-+  ^----+----+----+-|-+  ^----+----+----+---+
                 |    |                |    |
                 +----+                +----+
                                      new_link

如果在创建 new_link 时 DMA 通道停止,则_不会_重新启动。 热链不会破坏 dma_async_issue_pending() 用于确保实际启动传输的假设。

此规则的一个例外

  • 如果 Buffer1 和 Buffer2 的所有地址都与 8 字节对齐

  • 并且如果 Buffer3 至少有一个地址与 4 字节不对齐

  • 那么不能发生热链,因为必须停止通道,必须设置“对齐位”,并且必须重新启动通道。因此,这样的传输 tx_submit() 将在提交的队列中排队,并且如果 DMA 已经在对齐模式下运行,则会发生这种情况。

d) 传输完成更新程序:每次在通道上完成传输时,可能会生成也可能不生成中断,具体取决于客户端的请求。 但在每种情况下,传输的最后一个描述符“状态更新程序”会将完成的最新传输写入物理通道的完成标记中。

这将加快残余计算,对于大型传输,例如容纳 6k 或更多描述符的视频缓冲区。 这也允许在没有任何锁的情况下找出运行的 DMA 链中最新完成的传输是什么。

e) 传输完成、irq 和 tasklet:当标记为“DMA_PREP_INTERRUPT”的传输完成时,会引发 dma irq。 在此中断时,会为物理通道安排一个 tasklet。

tasklet 负责

  • 读取物理通道的最后一个更新标记

  • 根据该标记和每个传输标志,调用已完成传输的所有传输回调。

如果在完成此处理时完成了一个传输,则会引发 dma irq,并且将再次安排 tasklet,其中包含一个新的更新程序标记。

f) 残留:残留粒度将基于描述符。 将扫描已发出但未完成的传输,以针对当前正在运行的描述符查找所有描述符。

g) 驱动程序的 tx 队列中最复杂的情况:最棘手的情况是当

  • 没有“已确认”的传输 (tx0)

  • 驱动程序提交了一个对齐的 tx1,未链接

  • 驱动程序提交了一个对齐的 tx2 => tx2 冷链到 tx1

  • 驱动程序发出了 tx1+tx2 => 通道以对齐模式运行

  • 驱动程序提交了一个对齐的 tx3 => tx3 是热链的

  • 驱动程序提交了一个未对齐的 tx4 => tx4 被放入提交的队列中,未链接

  • 驱动程序发出了 tx4 => tx4 被放入已发出的队列中,未链接

  • 驱动程序提交了一个对齐的 tx5 => tx5 被放入提交的队列中,未链接

  • 驱动程序提交了一个对齐的 tx6 => tx6 被放入提交的队列中,冷链到 tx5

这转化为(在发出 tx4 之后)

  • 已发出队列

+-----+ +-----+ +-----+ +-----+
| tx1 | | tx2 | | tx3 | | tx4 |
+---|-+ ^---|-+ ^-----+ +-----+
    |   |   |   |
    +---+   +---+
  - submitted queue
+-----+ +-----+
| tx5 | | tx6 |
+---|-+ ^-----+
    |   |
    +---+
  • 已完成队列:空

  • 已分配队列:tx0

应该注意的是,在 tx3 完成后,通道停止,并以“未对齐模式”重新启动以处理 tx4。

作者:Robert Jarzmik <robert.jarzmik@free.fr>