vin驱动入口
vin驱动的入口文件是drivers\media\platform\sunxi-vin\vin.c,入口函数是vin_init():
static int __init vin_init(void)
{
ret = sunxi_csi_platform_register();
ret = sunxi_isp_platform_register();
ret = sunxi_mipi_platform_register();
ret = sunxi_flash_platform_register();
ret = sunxi_scaler_platform_register();
ret = sunxi_vin_core_register_driver();
ret = sunxi_vin_debug_register_driver();
ret = platform_driver_register(&vin_driver);
}
在vin_init()中分别注册csi、isp、scaler等驱动,这里简单分析csi、isp、scaler驱动。
CSI驱动
DST中有:
即系统有4个CSI模块,sunxi_csi_platform_register()调用后,将4次调用csi_probe(),这个函数主要完成以下任务:
分配并初始化csi_dev
- 解析DST
- 映射csi寄存器地址
- 初始化一个csi_list列表
- 调用__csi_init_subdev()
- 调用platform_set_drvdata()设置私有数据,方便驱动内部其他函数获得csi_dev
这里关注__csi_init_subdev():
static int __csi_init_subdev(struct csi_dev *csi)
{
struct v4l2_subdev *sd = &csi->subdev;
int ret;
mutex_init(&csi->subdev_lock);
csi->arrange.row = 1;
csi->arrange.column = 1;
csi->bus_info.ch_total_num = 1;
v4l2_subdev_init(sd, &sunxi_csi_subdev_ops);//初始化csi->subdev
sd->grp_id = VIN_GRP_ID_CSI;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
snprintf(sd->name, sizeof(sd->name), "sunxi_csi.%u", csi->id);
v4l2_set_subdevdata(sd, csi);//绑定csi到sd,即subdev与启动自定义结构体相互绑定
csi->csi_pads[CSI_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
csi->csi_pads[CSI_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE;
sd->entity.type = MEDIA_ENT_T_V4L2_SUBDEV;
ret = media_entity_init(&sd->entity, CSI_PAD_NUM, csi->csi_pads, 0);//初始化csi->subdev->entity
if (ret < 0)
return ret;
return 0;
}
这个函数首先调用v4l2_subdev_init初始化一个v4l2_subdev设备,它在后续将被添加到vin_mid. vin_csi_info[]里面,参数sunxi_csi_subdev_ops可以参考《nvp6134驱动.docx》中关于v4l2_subdev_ops结构体的说明。调用v4l2_set_subdevdata()实现sd绑定csi,其他地方可以调用v4l2_get_subdevdata()这样就会很方便的从v4l2_subdev找到csi设备数。这个函数最后调用media_entity_init()初始化一个Media Entity(实体):
调用csi_probe()后几个结构体的关系如下:
那这个csi的v4l2_subsdev再哪里添加到vin驱动的v4l2_device呢?
vin.c vin_probe()–>vin_md_register_entities()有:
首先调用sunxi_csi_get_subdev()(如下)获得系统注册的csi模块v4l2_subdev设备,然后调用v4l2框架函数v4l2_device_register_subdev()注册。
csi_drv_list在sunxi_csi.c中定义:
宏LIST_HEAD如下:
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
#define LIST_HEAD_INIT(name) { &(name), &(name) }
展开后,csi_drv_list的定义相当于:
struct list_head csi_drv_list= {
&csi_drv_list,
&csi_drv_list,
}
即定义了一个前向和后向都指向自己的list_head,且这是一个全局的变量,整个系统共享一个变量。而每个csi设备在调用csi_probe函数时都会调用:
将每个csi_dev查到列表尾部,这样sunxi_csi_get_subdev()就可以获得csi_dev给vin驱动使用了。
ISP驱动
DST中有:
即系统同样有4个ISP模块,sunxi_isp_platform_register()调用后,将4次调用isp_probe (),该函数与上面csi_probe()类似,但是多了一个v4l2_ctrl_handler的初始化和初始化isp_stat。
scaler驱动
系统有8个scaler模块,sunxi_isp_platform_register()调用后,将8次调用scaler_probe(),代码与csi_probe()类似,不再介绍。
MIPI驱动
代码:drivers\media\platform\sunxi-vin\vin-mipi
芯片中集成了两个MIPI-CSI接口,DST中配置如下:
其中设备的id定义为0x0或0x1,根据项目电路图,选择对应的MIPI-CSI接口:
因MIPI0只能输出给CSI0,所以配置vin0_csi_sel=0
vin-mipi驱动代码与csi驱动代码类似,不解释。
总结
总的来说,vin驱动的v4l2_subdev有
csi_dev->subdev
isp_dev->subdev
isp_stat-->subdev
scaler_dev-->subdev
vic_core->vid_cap.subdev
cci_driver-->subdev
这刚好对应vin_md结构体内部的几个数组(大部分v4l2驱动都是现实为链表的,这里因为知道subdev最大的数量,所以可以定义为数组形式):
struct vin_md {
struct vin_csi_info csi[VIN_MAX_CSI];
struct vin_mipi_info mipi[VIN_MAX_MIPI];
struct vin_cci_info cci[VIN_MAX_CCI];
struct vin_isp_info isp[VIN_MAX_ISP];
struct vin_stat_info stat[VIN_MAX_ISP];
struct vin_scaler_info scaler[VIN_MAX_SCALER];
。。。。。。
}