定义理解
Camera ISP 场景
Camera ISP 场景下在 ISP → Memory 的数据流里:ISP 内部有 DMA 引擎(通常叫 ISP output DMA / Write DMA)。
ISP 出一帧时,数据通过 内部 DMA 硬件写入内存。
一帧传完,ISP 会产生“frame done”中断,有些 SoC 把这个中断叫做 ISP 中断,本质上是 ISP 内部 DMA 完成触发的中断。
也就是说:在 Camera ISP 里,应用看到的中断不是“通用 DMA 控制器的中断”,而是 ISP 内部的帧完成中断。
但它的意义和 DMA 完成中断是一样的:告诉 CPU 有一帧写好了,可以 DQBUF 了。
驱动层处理逻辑
ISP/DMA 完成一帧 → 硬件产生中断 → GIC 送到 CPU。
驱动的中断 handler 被调用。驱动在 handler 里标记该 buffer 已完成 → 之后把它放入 done 队列;
唤醒等待队列(select/poll/unblock DQBUF), 应用 VIDIOC_DQBUF → 成功取到这一帧。
如果中断没来 → buffer 永远不会进 done 队列 → 应用只能报 EAGAIN。
总结
DMA 完成一定会有中断,否则 CPU/驱动不知道哪块 buffer 填好了。
在 ISP 这种专用硬件里,这个中断通常以 ISP frame done IRQ 的形式暴露出来(但实际上就是 DMA 完成事件)。
测试DMA读写模块测试
ISP_APB_DMA_TASK_TYPE
typedef struct ISP_APB_DMA_TASK_TYPE{uint32_t task_valid; //1:task enableuint32_t task_mode ; //0:auto clean task on done. 1:manualuint32_t task_type ; //0:read task 1:write taskuint32_t task_lens ; //rd:reg_num-1 wr:reg_num*2-1;uint32_t task_caddr; //command ddr addressuint32_t task_daddr; //rd cmd to ddr address
}ISP_APB_DMA_TASK_t;
ISP_APB_DMA_TASK_t 这个结构体其实就是一个 DMA 描述符(task descriptor),用来告诉 ISP 的 DMA 引擎:
要做一个任务(读/写)读/写多少寄存器,数据要从哪里来 ,要到哪里去;
读任务(task_type = 0)
DMA 任务作用:从寄存器 → DDR
task_caddr
存放要读的寄存器地址列表。
例如:0x1000, 0x1004, 0x1008 ...
DMA 会依次按照这些寄存器地址去 APB 读寄存器。
task_daddr
DMA 把读出来的数据写到 DDR 里的起始地址(即 task_daddr 指定的位置)。
最终 DDR 里的数据会长这样:
[reg0_value][reg1_value][reg2_value]...
写任务(task_type = 1)工作中一般用到写任务
DMA 任务作用:从 DDR → 寄存器
task_caddr :存放命令对: (寄存器地址 + 要写的值)。
通常一个写命令需要两个 word:[addr0][data0][addr1][data1][addr2][data2]...;所以 task_lens = reg_num * 2 - 1。
task_daddr:在 写任务 中通常不用(或者保留),因为数据已经和地址一起放在 task_caddr 的 DDR buffer 里了。
DMA 引擎只需要去 task_caddr 取 (寄存器地址, 数据) 对(根据实际要求填写buffer),依次写入寄存器。
对比总结
字段 | 读任务(read) | 写任务(write) |
---|---|---|
task_lens | reg_num - 1 | reg_num * 2 - 1 |
task_caddr | 存放寄存器地址列表 | 存放 (寄存器地址, 数据) 对,或者(数据,寄存器地址、或者地址偏移量等) |
task_daddr | 存放寄存器值的 DDR 地址 | 通常不用(数据在 caddr 里) |
注释
task_caddr = 任务的命令区(告诉 DMA 要操作哪些寄存器 / 要写哪些数据)
task_daddr = 数据存放区(只在读任务时真正使用,写的时候通常不用)
- RDMA(读任务,task_type=0)
reg_num = 你要读的寄存器个数。
DMA 控制器每次按 "一个寄存器的宽度(比如 4字节)" 去搬数据。
所以 task_lens 配置时,告诉硬件 "我要读多少个寄存器 - 1"。task_lens = reg_num - 1;
比如要读 4 个寄存器,就配 task_lens = 3。
原因是很多硬件寄存器里的长度字段定义是 "实际长度 - 1",因为 0 就代表 1 个元素,避免出现歧义。
- WDMA(写任务,task_type=1)
写的时候不只是写寄存器值,还需要告诉硬件 "写哪个寄存器 + 写的值"。所以一个寄存器写操作,实际上需要 两个 32-bit 命令字:
地址字段(寄存器地址)4字节 + 数据字段(要写的值) 4字节
因此 一个寄存器对应两个 word。task_lens = reg_num * 2 - 1;
比如要写 4 个寄存器,需要 8 个 word → task_lens = 7。
举例说明点击查看代码
*//wr src0
./devmem3 w 0x78c40000 0x1234567 //第一个四字节,先写值再写地址;
./devmem3 w 0x78c40004 0x000000 //第二个四字节, 0xff00 0000 基地址,编译地址0x000000 ,最终地址是 0xff00 0000 ./devmem3 w 0xff010404 0x0 //MIPI_DMA_CTL0
./devmem3 w 0xff0100c8 0x0
./devmem3 w 0xff0100cc 0x0
./devmem3 w 0xff010400 0x100109./devmem3 w 0xff0104c0 0x1
./devmem3 w 0xff0104c0 0x5
./devmem3 w 0xff0100c0 0x0./devmem3 w 0xff0104c4 0x78c40000 //task_caddr
./devmem3 w 0xff0104cC 0xC0000001 // MIPI_DMA_SRC0_PING_TASK0 读写属性 读写长度等./devmem3 w 0xff010404 0x80000000./devmem3 w 0xff010410 0x0 ./devmem3 w 0xff0100c8 0x1
./devmem3 w 0xff0100c8 0x10001 //MIPI_ISP_RDMA_CTRL0 dma_trig./devmem3 r 0xff000000
./devmem3 r 0xff010408 //读取中断状态 如果完成 则是1 表示中断正常
./devmem3 r 0xff0104cC
=================================================
*//wr src1
./devmem3 w 0xff000000 0x0./devmem3 w 0x78c40000 0x1234567
./devmem3 w 0x78c40004 0x00000./devmem3 w 0xff010404 0x0
./devmem3 w 0xff0100c8 0x2
./devmem3 w 0xff0100cc 0x0
./devmem3 w 0xff010400 0x100109./devmem3 w 0xFF0104F4 0x1
./devmem3 w 0xFF0104F4 0x5
./devmem3 w 0xff0100c0 0x1./devmem3 w 0xFF0104F8 0x78c40000
./devmem3 w 0xff0104cc 0xC0000001./devmem3 w 0xff010404 0x80000000./devmem3 w 0xff010410 0x0./devmem3 w 0xff0100c8 0x1
./devmem3 w 0xff0100c8 0x10001./devmem3 r 0xff000000
./devmem3 r 0xff010408
./devmem3 r 0xff0104cC