关于qcom camera cpp driver 的buf分析笔记(msm8996)
tags : linux camera
文章目录
- 关于qcom camera cpp driver 的buf分析笔记(msm8996)
- @[toc]
- CPP工作过程
- 1 `cpp_open_node()`
- 2 `VIDIOC_MSM_CPP_GET_INST_INFO32`
- 3 `VIDIOC_SUBSCRIBE_EVENT`
- 4 `VIDIOC_MSM_CPP_IOMMU_ATTACH32 --> VIDIOC_MSM_CPP_IOMMU_ATTACH`
- 5 `VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32 --> VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO`
- 6 `VIDIOC_MSM_CPP_SET_CLOCK32 --> VIDIOC_MSM_CPP_SET_CLOCK`
- 7 `VIDIOC_MSM_CPP_CFG32`
- 8 cpp irq : `IRQF_TRIGGER_RISING"`
- 内存使用流程
- 这里记录一下关于msm vb2相关操作的来源
文章目录
- 关于qcom camera cpp driver 的buf分析笔记(msm8996)
- @[toc]
- CPP工作过程
- 1 `cpp_open_node()`
- 2 `VIDIOC_MSM_CPP_GET_INST_INFO32`
- 3 `VIDIOC_SUBSCRIBE_EVENT`
- 4 `VIDIOC_MSM_CPP_IOMMU_ATTACH32 --> VIDIOC_MSM_CPP_IOMMU_ATTACH`
- 5 `VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32 --> VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO`
- 6 `VIDIOC_MSM_CPP_SET_CLOCK32 --> VIDIOC_MSM_CPP_SET_CLOCK`
- 7 `VIDIOC_MSM_CPP_CFG32`
- 8 cpp irq : `IRQF_TRIGGER_RISING"`
- 内存使用流程
- 这里记录一下关于msm vb2相关操作的来源
CPP工作过程
其实cpp的工作过程就是一个伴随着buf流转的过程,这里节约时间,以最简便的方式把主线过程记录下来。
1 cpp_open_node()
主要做了两件事:1、初始化硬件,2、初始化mem。
cpp_init_hardware()
:设置一些硬件参数、时钟、注册中断以及buf管理接口:msm_cam_buf_mgr_register_ops()
cpp_init_mem()
: 其实就是获取cpp_dev->iommu_hdl
,这个东西是在msm_cam_smmu
设备driver中统一管理的,vfe中有记录过这里。
2 VIDIOC_MSM_CPP_GET_INST_INFO32
user space获取cpp参数,具体是干什么结合android侧分析的时候再记录。
3 VIDIOC_SUBSCRIBE_EVENT
v4l2中的event系统,这里十之八九是订阅的V4L2_EVENT_CPP_FRAME_DONE
事件……其实加个打印看一眼就可以确定了……
4 VIDIOC_MSM_CPP_IOMMU_ATTACH32 --> VIDIOC_MSM_CPP_IOMMU_ATTACH
int arm_iommu_attach_device(struct device *dev,
struct dma_iommu_mapping *mapping)
{
int err;
err = iommu_attach_device(mapping->domain, dev);
if (err)
return err;
kref_get(&mapping->kref);
dev->archdata.mapping = mapping;
set_dma_ops(dev, &iommu_ops);
pr_debug("Attached IOMMU controller to %s device.\n", dev_name(dev));
return 0;
}
1、调用domain的attach
2、设置dev的iommu操作为:iommu_ops
关于这块在VFE中已经有记录。
5 VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32 --> VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO
msm_cpp_subdev_ioctl
{
……
switch (cmd) {
……
case VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO: {//198
……
/* 从cpp_dev->buff_queue[i]中找到指定的buff queue */
buff_queue_info = msm_cpp_get_buff_queue_entry(cpp_dev,
(k_stream_buff_info.identity >> 16) & 0xFFFF,
k_stream_buff_info.identity & 0xFFFF);
/* 如果指定的buff queue不存在则添加这个buff queue */
if (buff_queue_info == NULL) {
/* 从cpp_dev->buff_queue[i]中找到一个没用的buff queue */
rc = msm_cpp_add_buff_queue_entry(cpp_dev,
((k_stream_buff_info.identity >> 16) & 0xFFFF),
(k_stream_buff_info.identity & 0xFFFF));
……
}
= ……
if (VIDIOC_MSM_CPP_DELETE_STREAM_BUFF == cmd) {
……
} else {
for (j = 0; j < k_stream_buff_info.num_buffs; j++) {
/* 逐条把每个buf info加入到该buff queue中,这里是用ion分配的buf */
msm_cpp_queue_buffer_info(cpp_dev,
buff_queue_info,
&k_stream_buff_info.buffer_info[j]);
}
}
}
……
}
……
}
6 VIDIOC_MSM_CPP_SET_CLOCK32 --> VIDIOC_MSM_CPP_SET_CLOCK
设置时钟……
7 VIDIOC_MSM_CPP_CFG32
这里就是开始工作了,通过VIDIOC_MSM_CPP_CFG32
给cpp喂一帧数据,然后cpp吐一帧数据。
static int msm_cpp_cfg_frame(struct cpp_device *cpp_dev,
struct msm_cpp_frame_info_t *new_frame)
{
……
/* 把input buff的物理地址通过iommu映射给cpp的va,返回的phyaddr其实是cpp的va */
in_phyaddr = msm_cpp_fetch_buffer_info(cpp_dev,
&new_frame->input_buffer_info,
((new_frame->input_buffer_info.identity >> 16) & 0xFFFF),
(new_frame->input_buffer_info.identity & 0xFFFF), &in_fd);
……
/* 把output buff的物理地址通过iommu映射给cpp的va,返回的phyaddr其实是cpp的va
* output的buff在前面“VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO”时已经映射好了,这里直接读
*/
out_phyaddr0 = msm_cpp_fetch_buffer_info(cpp_dev,
&new_frame->output_buffer_info[0],
((iden >> 16) & 0xFFFF),
(iden & 0xFFFF),
&new_frame->output_buffer_info[0].fd);
……
/* 把input buf 和 output buf的地址写给cpp,这里是把cpp所看到的va写下去了 */
msm_cpp_update_frame_msg_phy_address(cpp_dev, new_frame,
in_phyaddr, out_phyaddr0, out_phyaddr1,
tnr_scratch_buffer0, tnr_scratch_buffer1);
……
}
8 cpp irq : IRQF_TRIGGER_RISING"
当cpp处理完一帧数据后,产生该中断,通知相关的模块来取数据。
msm_cpp_irq() --> tasklet_schedule(&cpp_dev->cpp_tasklet) --> msm_cpp_do_tasklet() --> msm_cpp_notify_frame_done() --> msm_enqueue(&cpp_dev->eventData_q, ...) --> v4l2_event_queue(V4L2_EVENT_CPP_FRAME_DONE)
通过v4l2 event机制通知订阅了V4L2_EVENT_CPP_FRAME_DONE
消息的模块来取数据
VIDIOC_DQEVENT
(89)
相关模块把数据读走
VIDIOC_MSM_CPP_GET_EVENTPAYLOAD32
(193)
内存使用流程
关于msm8996平台下,cpp driver中buf的管理过程记录一下,除vfe模块外,vpe模块的管理模式也是这样。由于msm8996平台较老,从835平台开始这一块的变化就比较大,这里等有了新平台再对新平台进行分析。
先是整个buf相关的管理过程序列图:
这里cpp的input buf是vfe的Output buf,在其他情况下也可以不是。cpp的output buf是对cpp进行配置时通过VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32
来下发的,这点跟vfe模块一样,所有的输入输出buf都是有ion分配出来的。
这里output buf的index是由msm_vb2
来决定的,vb2通过VIDIOC_REQBUFS
进行设置的,而cpp的cpp_dev->buff_queue[i]->native_buff_head
通过VIDIOC_MSM_CPP_ENQUEUE_STREAM_BUFF_INFO32
获取,这里cpp_dev
中的buf管理有一个重要的作用就是cam_smmu_get_phy_addr()
。get phy addr这个东西在VFE中已经记录过了,就是把这个ion分配的内存通过smmu映射到CPP的虚拟地址上面去,这里获取的所谓的phy addr其实就是cpp的虚拟地址。
至于android那边是如何管理这两个内存的,等研究过那部分代码后再单独记录,目前猜测应该都源自同一个ion分配的内存,并且两者要保持完全的一致。
这里记录一下关于msm vb2相关操作的来源
相关文件:msm.c
相关函数:
static int msm_probe(struct platform_device *pdev)
{
……
msm_v4l2_dev->notify = msm_sd_notify;
pvdev->vdev->v4l2_dev = msm_v4l2_dev;
……
}
相关文件:msm_generic_buf_mgr.c
相关函数:
static int32_t __init msm_buf_mngr_init(void)
{
……
v4l2_subdev_notify(&msm_buf_mngr_dev->subdev.sd, MSM_SD_NOTIFY_REQ_CB,
&msm_buf_mngr_dev->vb2_ops);
……
}
这里的v4l2_subdev_notify
最终就是调用的msm_sd_notify()
。