1 核心、常用结构体
1.1 struct v4l2_device
struct v4l2_device {
/* dev->driver_data points to this struct.
Note: dev might be NULL if there is no parent device
as is the case with e.g. ISA devices. */
struct device *dev; //设备模型dev
#if defined(CONFIG_MEDIA_CONTROLLER)
struct media_device *mdev; //媒体设备,指向一个媒体控制器的指针
#endif
/* used to keep track of the registered subdevs */
struct list_head subdevs; //保存所有子设备的链表
/* lock this struct; can be used by the driver as well if this
struct is embedded into a larger struct. */
spinlock_t lock; //锁
/* unique device name, by default the driver name + bus ID */
char name[V4L2_DEVICE_NAME_SIZE]; //独一无二的名字,默认是驱动名+总线ID
/* notify callback called by some sub-devices. */
void (*notify)(struct v4l2_subdev *sd,
unsigned int notification, void *arg); //通知链回调函数,被子设备调用
/* The control handler. May be NULL. */
struct v4l2_ctrl_handler *ctrl_handler; //控制句柄
/* Device's priority state */
struct v4l2_prio_state prio; //设备的优先级状态,一般有后台,交互,记录三种优先级,依次变高
/* BKL replacement mutex. Temporary solution only. */
struct mutex ioctl_lock; //ioctl操作的互斥量
/* Keep track of the references to this struct. */
struct kref ref; //引用计数
/* Release function that is called when the ref count goes to 0. */
void (*release)(struct v4l2_device *v4l2_dev); //设备释放函数
};
struct v4l2_device在v4l2子系统中处理核心位置,链表subdevs保存所有v4l2子设备,v4l2_device通常会被嵌入另一个特定的结构体,根据不同cpu厂商做特定修改来来确定。在s5p6818中,v4l2_device被嵌入到nxp_v4l2中
1.2 struct nxp_v4l2
struct nxp_v4l2 {
struct media_device media_dev; //媒体设备
struct v4l2_device v4l2_dev; //v4l2 dev
struct platform_device *pdev; //平台设备
struct nxp_v4l2_platformdata *pdata; //平台数据
/* child */
#ifdef CONFIG_VIDEO_NXP_CAPTURE
struct nxp_capture *capture[NXP_MAX_CAPTURE_NUM]; //capture设备,也就是camera
#endif
#ifdef CONFIG_NXP_M2M_SCALER
struct nxp_scaler *scaler;
#endif
#ifdef CONFIG_VIDEO_NXP_OUT
struct nxp_out *out;
#endif
#ifdef CONFIG_LOOPBACK_SENSOR_DRIVER
struct nxp_loopback_sensor *loopback_sensor;
#endif
void *alloc_ctx; //分配内存上下文
};
nxp v4l2会以平台驱动方式注册,capture数组保存两个camera数据。
1.3 struct media_device
struct media_device {
/* dev->driver_data points to this struct. */
struct device *dev; //media_device父设备
struct media_devnode devnode; //设备结点
char model[32]; //model name 模块名
char serial[40]; //设备串口号,可选
char bus_info[32]; //总线信息
u32 hw_revision;
u32 driver_version;
u32 entity_id;
struct list_head entities; //已注册媒体设备实体
/* Protects the entities list */
spinlock_t lock; //保护上来链表的锁
/* Serializes graph operations. */
struct mutex graph_mutex;
int (*link_notify)(struct media_pad *source,
struct media_pad *sink, u32 flags); //媒体设备连接上后的回调函数
};
1.4 struct media_entity
struct media_entity {
struct list_head list; /* 将entity加到media_device的链表中 */
struct media_device *parent; /* 所属媒体设备,就是上面的struct media_device */
u32 id; /* Entity ID, unique in the parent media * device context, 在meida_device中,这个ID是唯一的 */
const char *name; /* Entity name 实体名 */
u32 type; /* Entity type (MEDIA_ENT_T_*) 类型 */
u32 revision; /* Entity revision, driver specific */
unsigned long flags; /* Entity flags (MEDIA_ENT_FL_*) */
u32 group_id; /* Entity group ID 组ID,极少用 */
u16 num_pads; /* Number of sink and source pads entity所包含pad的个数 */
u16 num_links; /* Number of existing links, both* enabled and disabled link个数,包含使能与禁用的 */
u16 num_backlinks; /* Number of backlinks */
u16 max_links; /* Maximum number of links link的最大个数,可以调整 */
struct media_pad *pads; /* Pads array (num_pads elements),pad数组,通常是两个元素,souce pad sink pad */
struct media_link *links; /* Links array (max_links elements) link数组,保存所有连接到source pad的link */
const struct media_entity_operations *ops; /* entity的ops */
/* Reference counts must never be negative, but are signed integers on
* purpose: a simple WARN_ON(<0) check can be used to detect reference
* count bugs that would make them negative.
*/
int stream_count; /* Stream count for the entity. */
int use_count; /* Use count for the entity. */
struct media_pipeline *pipe; /* Pipeline this entity belongs to. */
union {
/* Node specifications */
struct {
u32 major;
u32 minor;
} v4l;
struct {
u32 major;
u32 minor;
} fb;
struct {
u32 card;
u32 device;
u32 subdevice;
} alsa;
int dvb;
/* Sub-device specifications */
/* Nothing needed yet */
} info;
};
struct media_entity中所包含的结构体
struct media_link {
struct media_pad *source; /* Source pad */
struct media_pad *sink; /* Sink pad */
struct media_link *reverse; /* Link in the reverse direction */
unsigned long flags; /* Link flags (MEDIA_LNK_FL_*) */
};
struct media_pad {
struct media_entity *entity; /* 所属Entity */
u16 index; /* 当前pad在pads数组中的index */
unsigned long flags; /* Pad flags (MEDIA_PAD_FL_*) */
};
struct media_entity_operations {/* 只有一个回调函数,用来连接两个entity */
int (*link_setup)(struct media_entity *entity,const struct media_pad *local, const struct media_pad *remote, u32 flags);
};
2 平台特有结构体
2.1
3 v4l2注册流程
在s5p6818中v4l2是以平台总线方式注册,平台驱动
static struct platform_driver nxp_v4l2_driver = {
.probe = nxp_v4l2_probe, //probe函数
.remove = nxp_v4l2_remove,
.id_table = nxp_v4l2_id_table,
.driver = {
.name = NXP_V4L2_DEV_NAME,
.owner = THIS_MODULE,
.pm = NXP_V4L2_PMOPS,
},
};
probe函数部分代码,有几个重要的流程分支
static int __devinit nxp_v4l2_probe(struct platform_device *pdev)
{
struct v4l2_device *v4l2_dev; //v4l2核心结构体
struct nxp_v4l2 *nxp_v4l2; //顶层结构体
struct nxp_v4l2_platformdata *pdata; //平台数据
#ifdef CONFIG_VIDEO_NXP_CAPTURE
struct nxp_capture_platformdata *capture_pdata; //camera平台数据,在pdata中
struct nxp_capture *capture;
int i;
#endif
#ifdef CONFIG_NXP_M2M_SCALER
struct nxp_scaler *scaler;
#endif
int ret;
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "can't get platformdata\n");
return -EINVAL;
}
nxp_v4l2 = kzalloc(sizeof(*nxp_v4l2), GFP_KERNEL); //分配nxp_v4l2内存
if (!nxp_v4l2) {
pr_err("%s error: fail to kzalloc(size %d)\n", __func__, sizeof(struct nxp_v4l2));
return -ENOMEM;
}
nxp_v4l2->pdev = pdev;
nxp_v4l2->pdata = pdata;
snprintf(nxp_v4l2->media_dev.model, sizeof(nxp_v4l2->media_dev.model), "%s",
dev_name(&pdev->dev)); //media_device的模块名与平台设备名字保持一致
nxp_v4l2->media_dev.dev = &pdev->dev; //媒体设备父设备为平台设备,设备模型dev
v4l2_dev = &nxp_v4l2->v4l2_dev;
v4l2_dev->mdev = &nxp_v4l2->media_dev; //指向同一个media设备
snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s",
dev_name(&pdev->dev));
/* 1 内存分配上下文 alloc context : use uncached area 内存分配上下文 */
nxp_v4l2->alloc_ctx =
vb2_ion_create_context(&pdev->dev, SZ_4K, VB2ION_CTX_UNCACHED);
if (!nxp_v4l2->alloc_ctx) {
pr_err("%s: failed to ion alloc context\n", __func__);
ret = -ENOMEM;
goto err_alloc_ctx;
}
ret = v4l2_device_register(&pdev->dev, &nxp_v4l2->v4l2_dev); //实际并未注册v4l2设备,仅完成v4l2初始化工作
if (ret < 0) {
pr_err("%s: failed to register v4l2_device: %d\n", __func__, ret);
goto err_v4l2_reg;
}
ret = media_device_register(&nxp_v4l2->media_dev); //2 media设备注册
if (ret < 0) {
pr_err("%s: failed to register media_device: %d\n", __func__, ret);
goto err_media_reg;
}
__me = nxp_v4l2; //将nxp_v4l2保存到全局指针__me
#ifdef CONFIG_VIDEO_NXP_CAPTURE
/* capture */
for (capture_pdata = pdata->captures, i = 0;
capture_pdata->sensor;
capture_pdata++, i++) {
/* TODO : psw0523 test for module index problem !!! */
/* capture = create_nxp_capture(i, capture_pdata); */ /* 3 capture create */
capture = create_nxp_capture(i, capture_pdata->module, capture_pdata);
if (!capture) {
pr_err("%s: failed to %dth create_nxp_capture()\n", __func__, i);
ret = -EINVAL;
goto err_capture_create;
}
ret = register_nxp_capture(capture); /* 4 capture 注册*/
if (ret < 0) {
pr_err("%s: failed to %dth register_nxp_capture()\n", __func__, i);
goto err_capture_create;
}
nxp_v4l2->capture[i] = capture;
}
#endif
#ifdef CONFIG_NXP_M2M_SCALER
/* m2m ,5 scaler的创建与注册 */
scaler = create_nxp_scaler();
if (!scaler) {
pr_err("%s: failed to create_nxp_scaler()\n", __func__);
ret = -ENOMEM;
#ifdef CONFIG_VIDEO_NXP_CAPTURE
goto err_capture_create;
#else
goto err_media_reg;
#endif
}
ret = register_nxp_scaler(scaler);
if (ret < 0) {
pr_err("%s: failed to nxp_scaler_register()\n", __func__);
goto err_register_scaler;
}
nxp_v4l2->scaler = scaler;
#endif
#ifdef CONFIG_VIDEO_NXP_OUT
/* out */ /* 不需要关注out相关内容,跟hdmi有关 */
nxp_v4l2->out = create_nxp_out(pdata->out);
if (!nxp_v4l2->out) {
pr_err("%s: failed to create_nxp_out()\n", __func__);
goto err_create_out;
}
ret = register_nxp_out(nxp_v4l2->out);
if (ret < 0) {
pr_err("%s: failed to register_nxp_out()\n", __func__);
goto err_register_out;
}
#endif
/* 6 v4l2子设备注册 */
ret = v4l2_device_register_subdev_nodes(&nxp_v4l2->v4l2_dev);
if (ret < 0) {
pr_err("%s: failed to v4l2_device_register_subdev_nodes()\n", __func__);
goto err_register_out_subdev;
}
platform_set_drvdata(pdev, nxp_v4l2);
printk("%s success\n", __func__);
return 0;
err_register_out_subdev:
#ifdef CONFIG_VIDEO_NXP_OUT
unregister_nxp_out(nxp_v4l2->out);
err_register_out:
release_nxp_out(nxp_v4l2->out);
err_create_out:
#endif
#ifdef CONFIG_NXP_M2M_SCALER
unregister_nxp_scaler(scaler);
err_register_scaler:
release_nxp_scaler(scaler);
#endif
#ifdef CONFIG_VIDEO_NXP_CAPTURE
err_capture_create:
for (i = 0; i < NXP_MAX_CAPTURE_NUM; ++i) {
capture = nxp_v4l2->capture[i];
if (capture) {
unregister_nxp_capture(capture);
release_nxp_capture(capture);
}
}
media_device_unregister(&nxp_v4l2->media_dev);
#endif
#ifdef CONFIG_LOOPBACK_SENSOR_DRIVER
err_loopback_sensor_create:
if( loopback_sensor )
{
unregister_nxp_loopback_sensor(loopback_sensor);
release_nxp_loopback_sensor(loopback_sensor);
}
#endif
err_media_reg:
v4l2_device_unregister(&nxp_v4l2->v4l2_dev);
err_v4l2_reg:
vb2_ion_destroy_context(nxp_v4l2->alloc_ctx);
err_alloc_ctx:
kfree(nxp_v4l2);
__me = NULL;
return ret;
}
3.1 vb2_ion_create_context
void *vb2_ion_create_context(struct device *dev, size_t alignment, long flags)
{
struct vb2_ion_context *ctx;
unsigned int heapmask = ion_heapflag(flags);
struct ion_device *ion_dev = get_global_ion_device(); //全局ion dev
if (!ion_dev) {
pr_err("%s error: can't get global ion device!!!\n", __func__);
return ERR_PTR(-EINVAL);
}
/*
* only support continuous memory
*/
if (flags & VB2ION_CTX_VMCONTIG) {
pr_err("%s error: not support vmalloc ion context\n", __func__);
return ERR_PTR(-EINVAL);
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); //分配ctx内存
if (!ctx) {
pr_err("%s error: fail to kzalloc size: %d\n", __func__, sizeof(*ctx));
return ERR_PTR(-ENOMEM);
}
ctx->dev = dev;
ctx->client = ion_client_create(ion_dev, dev_name(dev)); //创建ion client
if (IS_ERR(ctx->client)) {
void *retp = ctx->client;
kfree(ctx);
return retp;
}
vb2_ion_set_alignment(ctx, alignment);
return ctx;
}
vb2_ion_create_context函数仅实现了分配内存(ion方式),并对ctx部分成员初始化
3.2 media_device_register
int __must_check media_device_register(struct media_device *mdev)
{
int ret;
if (WARN_ON(mdev->dev == NULL || mdev->model[0] == 0))
return -EINVAL;
mdev->entity_id = 1;
INIT_LIST_HEAD(&mdev->entities);
spin_lock_init(&mdev->lock);
mutex_init(&mdev->graph_mutex);
/* Register the device node. 注册设备结点 */
mdev->devnode.fops = &media_device_fops;
mdev->devnode.parent = mdev->dev;
mdev->devnode.release = media_device_release;
ret = media_devnode_register(&mdev->devnode); //media设备注册
if (ret < 0)
return ret;
ret = device_create_file(&mdev->devnode.dev, &dev_attr_model);
if (ret < 0) {
media_devnode_unregister(&mdev->devnode);
return ret;
}
return 0;
}
媒体设备结点注册函数,这个函数主要完成三个工作
- 找到可用次设备号
- 注册字符设备
- 注册媒体设备(挂载到media总线上),生成/dev/media*结点
int __must_check media_devnode_register(struct media_devnode *mdev)
{
int minor;
int ret;
/* Part 1: Find a free minor number 找一个可用的次设备号 */
mutex_lock(&media_devnode_lock);
minor = find_next_zero_bit(media_devnode_nums, MEDIA_NUM_DEVICES, 0);
if (minor == MEDIA_NUM_DEVICES) {
mutex_unlock(&media_devnode_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
set_bit(minor, media_devnode_nums);
mutex_unlock(&media_devnode_lock);
mdev->minor = minor;
/* Part 2: Initialize and register the character device 初始化、注册字符设备 */
cdev_init(&mdev->cdev, &media_devnode_fops); /* open read write ioctrl /dev/media*结点时会调用media_devnode_fops的函数, 函数的主要实现或者说最终调用是mdev->devnode.fops = &media_device_fops */
mdev->cdev.owner = mdev->fops->owner;
ret = cdev_add(&mdev->cdev, MKDEV(MAJOR(media_dev_t), mdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
goto error;
}
/* Part 3: Register the media device 注册一个媒体设备(挂载在mediabus总线上),在系统中生成了/dev/mediaX节点 */
mdev->dev.bus = &media_bus_type; //media总线
mdev->dev.devt = MKDEV(MAJOR(media_dev_t), mdev->minor); //设备号
mdev->dev.release = media_devnode_release; //设备结点释放函数
if (mdev->parent)
mdev->dev.parent = mdev->parent;
dev_set_name(&mdev->dev, "media%d", mdev->minor);
ret = device_register(&mdev->dev); //注册设备
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto error;
}
/* Part 4: Activate this minor. The char device can now be used. */
set_bit(MEDIA_FLAG_REGISTERED, &mdev->flags);
return 0;
error:
cdev_del(&mdev->cdev);
clear_bit(mdev->minor, media_devnode_nums);
return ret;
}
在媒体设备中有一个很重要的成员entity,两个设备间相连接需要用到entity。连接两个entity需要设备函数media_entity_create_link
int media_entity_create_link(struct media_entity *source, u16 source_pad,
struct media_entity *sink, u16 sink_pad, u32 flags)
{
struct media_link *link;
struct media_link *backlink;
BUG_ON(source == NULL || sink == NULL);
BUG_ON(source_pad >= source->num_pads);
BUG_ON(sink_pad >= sink->num_pads);
link = media_entity_add_link(source); /* 给source entity的link分配内存 */
if (link == NULL)
return -ENOMEM;
link->source = &source->pads[source_pad]; /* 将两个entity连接起来,source 指向 sink */
link->sink = &sink->pads[sink_pad];
link->flags = flags;
/* Create the backlink. Backlinks are used to help graph traversal and
* are not reported to userspace.
*/
backlink = media_entity_add_link(sink); /* backlink与link实际上是一样的,backlink属于sink entity */
if (backlink == NULL) {
source->num_links--;
return -ENOMEM;
}
backlink->source = &source->pads[source_pad];
backlink->sink = &sink->pads[sink_pad];
backlink->flags = flags;
link->reverse = backlink;
backlink->reverse = link;
sink->num_backlinks++;
return 0;
}
当media_entity_create_link函数被调用时,会分配一个link内存,link->souce指向source pad,link->sink指向sink pad,并会分配backlink实现与link同样的功能。
两个entity连接示意图如下
每个entity都会有一个source pad、一个sink pad,link个数则不定,根据需要可能有多个,可能有一个,也可能没有。可以认为数据源的那个entity(输出数据),这是是entity1为souce entity,entity2为sink entity。link和backlink本质上并没有什么区别,只是link属于entity1,而backlink属于entity2。并且实际使用link,而backlink当作备份。
3.3 create_nxp_capture
struct nxp_capture *create_nxp_capture(int index,int module,struct nxp_capture_platformdata *pdata)
{
struct nxp_capture *me;
int ret;
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
bool csi_enabled = pdata->type == NXP_CAPTURE_INF_CSI; //mipi camera需要设置这个类型
#endif
me = kzalloc(sizeof(*me), GFP_KERNEL); //分配nxp_capture大小空间保存一个camera的信息
if (!me) {
pr_err("%s: failed to allocate me\n", __func__);
return NULL;
}
INIT_LIST_HEAD(&me->irq_entry_list); //中断函数链表,可能会有多个被调用
spin_lock_init(&me->lock); //链表保护锁
me->index = index; //第几个camera,android一般有两个,前后摄像头,新的双摄像头技术可能会出现一个系统中有四个camera
me->module = module; //使用的video input processor,6818有3个,使用的是哪个,参考cpu手册,mipi csi 仅vip1支持,但是mipi ov5645使用module = 0,原因未知,可能是手册有误
me->interface_type = pdata->type;
me->platdata = pdata;
/* initialize callback 初始化回调函数 */
me->get_media_device = _get_media_device;
me->get_v4l2_device = _get_v4l2_device;
me->get_module_num = _get_module_num;
me->get_index_num = _get_index_num;
me->get_alloc_ctx = _get_alloc_ctx;
me->run = _run;
me->stop = _stop;
me->register_irq_entry = _register_irq_entry;
me->unregister_irq_entry = _unregister_irq_entry;
me->get_csi_subdev = _get_csi_subdev;
me->get_sensor_subdev = _get_sensor_subdev;
#ifdef CONFIG_TURNAROUND_VIP_RESET
me->backup_reset_restore_register = _backup_reset_restore_register;
#endif
init_waitqueue_head(&me->wait_change_context); /* 等待队列头初始化,上下文改变时需要用到 */
me->clip_enable = false;
me->deci_enable = false;
me->context_changed = false;
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
/* init children */
if (csi_enabled) {
if (!pdata->csi) {
pr_err("%s: no csi platform data\n", __func__);
goto error_csi;
}
ret = nxp_csi_init(&me->csi, pdata->csi); /* csi初始化 */
if (ret < 0) {
pr_err("%s: failed to nxp_csi_init()\n", __func__);
goto error_csi;
}
}
#endif
ret = nxp_vin_clipper_init(&me->vin_clipper, &pdata->parallel); /* clipper 初始化,clipper用于修剪图片,比如只要图片中特定部分(上半部分),把其他剪掉 */
if (ret < 0) {
pr_err("%s: failed to nxp_vin_clipper_init()\n", __func__);
goto error_vin;
}
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR
ret = nxp_decimator_init(&me->decimator, &pdata->deci); /* decimator初始化,比例缩放,当显示的图片太大时,使用decimator可使图片比例减小 */
if (ret < 0) {
pr_err("%s: failed to nxp_decimator_init()\n", __func__);
goto error_decimator;
}
#endif
/* create link */
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
if (csi_enabled) { /* cis->sudev->entity与clipper->sudev->entity相连接 */
ret = media_entity_create_link(
&me->csi.subdev.entity, NXP_CSI_PAD_SOURCE,
&me->vin_clipper.subdev.entity, NXP_VIN_PAD_SINK, 0);
if (ret < 0) {
pr_err("%s: failed to link csi source to vin sink\n", __func__);
goto error_link;
}
}
_set_sensor_mipi_info(index, csi_enabled);
#else
_set_sensor_mipi_info(index, 0);
#endif
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR /* decimator->sudev->entity与clipper->sudev->entity相连接 */
ret = media_entity_create_link(
&me->vin_clipper.subdev.entity, NXP_VIN_PAD_SOURCE_DECIMATOR,
&me->decimator.subdev.entity, NXP_DECIMATOR_PAD_SINK, 0);
if (ret < 0) {
pr_err("%s: failed to link vin source to decimator sink\n", __func__);
goto error_link;
}
#endif
/*
* sysfs
*/
create_sensor_sysfs(me->module);
vmsg("%s exit(%p)\n", __func__, me);
return me;
error_link:
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
if (csi_enabled)
nxp_csi_cleanup(&me->csi);
error_csi:
#endif
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR
nxp_decimator_cleanup(&me->decimator);
error_decimator:
#endif
nxp_vin_clipper_cleanup(&me->vin_clipper);
error_vin:
kfree(me);
return NULL;
}
函数nxp_vin_clipper_init中调用了_init_entities,_init_entities中两个两部分较为重要的
/* clipper下创建一个nxp_video,后面注册时将会生成/dev/video*结点 */
me->video = create_nxp_video(dev_name, NXP_VIDEO_TYPE_CAPTURE,
parent->get_v4l2_device(parent), parent->get_alloc_ctx(parent));
if (!me->video) {
pr_err("%s: failed to create_nxp_video()\n", __func__);
ret = -EINVAL;
goto error_video;
}
/* 创建link,将clipper->sudev->entity和nxp_video->vdev.entity连接起来,数据从clipper流向nxp_video->vdev */
ret = media_entity_create_link(entity, NXP_VIN_PAD_SOURCE_MEM,
&me->video->vdev.entity, 0, 0);
if (ret < 0) {
pr_err("%s: failed to media_entity_create_link()\n", __func__);
goto error_link;
}
函数nxp_decimator_init(&me->decimator, &pdata->deci); 逻辑架构与nxp_vin_clipper_init是一样的,只是在部分细节上有区分,同样调用了_init_entities(与clipper上的函数不一样)函数,创建nxp_video,分配link空间,将decimator->subdev->entity与nxp_video->vdev.entity连接起来。
这里的nxp_video是属于decimator的,跟clipper上的nxp_video并不是同一个。
3.4 register_nxp_capture
int register_nxp_capture(struct nxp_capture *me)
{
int ret;
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
bool csi_enabled = me->interface_type == NXP_CAPTURE_INF_CSI;
#endif
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
if (csi_enabled) {
ret = nxp_csi_register(&me->csi); /* csi注册 */
if (ret < 0) {
pr_err("%s: failed to nxp_csi_register()\n", __func__);
return ret;
}
}
#endif
ret = nxp_vin_clipper_register(&me->vin_clipper); /* clipper 注册 */
if (ret < 0) {
pr_err("%s: failed to nxp_vin_clipper_register()\n", __func__);
goto error_vin;
}
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR
ret = nxp_decimator_register(&me->decimator); /* decimator 注册 */
if (ret < 0) {
pr_err("%s: failed to nxp_decimator_register()\n", __func__);
goto error_decimator;
}
#endif
if (NULL == _register_sensor(me, me->platdata->sensor)) {
pr_err("%s: can't register sensor subdev\n", __func__);
goto error_sensor;
}
/* capture中断注册 */
ret = request_irq(me->irq, &_irq_handler, IRQF_SHARED, "nxp-capture", me);
if (ret){
pr_err("%s: failed to request_irq()\n", __func__);
goto error_irq;
}
return 0;
error_irq:
_unregister_sensor(me);
error_sensor:
#ifdef CONFIG_NXP_CAPTURE_DECIMATOR
nxp_decimator_unregister(&me->decimator);
error_decimator:
#endif
nxp_vin_clipper_unregister(&me->vin_clipper);
error_vin:
#ifdef CONFIG_NXP_CAPTURE_MIPI_CSI
if (csi_enabled)
nxp_csi_unregister(&me->csi);
#endif
return ret;
}
clipper注册
int nxp_vin_clipper_register(struct nxp_vin_clipper *me)
{
int ret;
struct nxp_capture *parent = nxp_vin_to_parent(me);
vmsg("%s\n", __func__);
ret = v4l2_device_register_subdev(parent->get_v4l2_device(parent),
&me->subdev); /* 将clipper->subdev添加到顶层v4l2_device设备的子设备链表中 */
if (ret < 0) {
pr_err("%s: failed to v4l2_device_register_subdev()\n", __func__);
return ret;
}
ret = register_nxp_video(me->video); /* 注册一个video设备,生成/dev/video*结点 */
if (ret < 0) {
pr_err("%s: failed to register_nxp_video()\n", __func__);
v4l2_device_unregister_subdev(&me->subdev);
}
return ret;
}
register_nxp_video函数实际上调用的是__video_register_device函数
/* 注册videos设备,生成/dev/video结点(camera为例) */
int __video_register_device(struct video_device *vdev, int type, int nr,
int warn_if_nr_in_use, struct module *owner)
{
int i = 0;
int ret;
int minor_offset = 0;
int minor_cnt = VIDEO_NUM_DEVICES;
const char *name_base;
/* A minor value of -1 marks this video device as never
having been registered */
vdev->minor = -1;
/* the release callback MUST be present */
if (WARN_ON(!vdev->release))
return -EINVAL;
/* v4l2_fh support */
spin_lock_init(&vdev->fh_lock);
INIT_LIST_HEAD(&vdev->fh_list);
/* Part 1: check device type */
switch (type) {
case VFL_TYPE_GRABBER:
name_base = "video"; //camera
break;
case VFL_TYPE_VBI:
name_base = "vbi";
break;
case VFL_TYPE_RADIO:
name_base = "radio";
break;
case VFL_TYPE_SUBDEV:
name_base = "v4l-subdev";
break;
default:
printk(KERN_ERR "%s called with unknown type: %d\n",
__func__, type);
return -EINVAL;
}
vdev->vfl_type = type;
vdev->cdev = NULL;
if (vdev->v4l2_dev) {
if (vdev->v4l2_dev->dev)
vdev->parent = vdev->v4l2_dev->dev;
if (vdev->ctrl_handler == NULL)
vdev->ctrl_handler = vdev->v4l2_dev->ctrl_handler;
/* If the prio state pointer is NULL, then use the v4l2_device
prio state. */
if (vdev->prio == NULL)
vdev->prio = &vdev->v4l2_dev->prio;
}
/* Part 2: find a free minor, device node number and device index. */
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons.
* Newer devices (not yet in place) should use the range
* of 128-191 and just pick the first free minor there
* (new style). */
switch (type) {
case VFL_TYPE_GRABBER:
minor_offset = 0;
minor_cnt = 64;
break;
case VFL_TYPE_RADIO:
minor_offset = 64;
minor_cnt = 64;
break;
case VFL_TYPE_VBI:
minor_offset = 224;
minor_cnt = 32;
break;
default:
minor_offset = 128;
minor_cnt = 64;
break;
}
#endif
/* Pick a device node number */
mutex_lock(&videodev_lock);
nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt);
if (nr == minor_cnt)
nr = devnode_find(vdev, 0, minor_cnt);
if (nr == minor_cnt) {
printk(KERN_ERR "could not get a free device node number\n");
mutex_unlock(&videodev_lock);
return -ENFILE;
}
#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of device node number to minor number */
i = nr;
#else
/* The device node number and minor numbers are independent, so
we just find the first free minor number. */
for (i = 0; i < VIDEO_NUM_DEVICES; i++)
if (video_device[i] == NULL)
break;
if (i == VIDEO_NUM_DEVICES) {
mutex_unlock(&videodev_lock);
printk(KERN_ERR "could not get a free minor\n");
return -ENFILE;
}
#endif
vdev->minor = i + minor_offset; //次设备号
vdev->num = nr; //设备结点序号,如video1, nr = 1
devnode_set(vdev);
/* Should not happen since we thought this minor was free */
WARN_ON(video_device[vdev->minor] != NULL);
vdev->index = get_index(vdev);
mutex_unlock(&videodev_lock);
/* Part 3: Initialize the character device */
vdev->cdev = cdev_alloc();
if (vdev->cdev == NULL) {
ret = -ENOMEM;
goto cleanup;
}
vdev->cdev->ops = &v4l2_fops;
vdev->cdev->owner = owner;
ret = cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
if (ret < 0) {
printk(KERN_ERR "%s: cdev_add failed\n", __func__);
kfree(vdev->cdev);
vdev->cdev = NULL;
goto cleanup;
}
/* Part 4: register the device with sysfs */
vdev->dev.class = &video_class;
vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);
if (vdev->parent)
vdev->dev.parent = vdev->parent;
dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num);
ret = device_register(&vdev->dev); //注册设备
if (ret < 0) {
printk(KERN_ERR "%s: device_register failed\n", __func__);
goto cleanup;
}
/* Register the release callback that will be called when the last
reference to the device goes away. */
vdev->dev.release = v4l2_device_release;
if (nr != -1 && nr != vdev->num && warn_if_nr_in_use)
printk(KERN_WARNING "%s: requested %s%d, got %s\n", __func__,
name_base, nr, video_device_node_name(vdev));
/* Increase v4l2_device refcount */
if (vdev->v4l2_dev)
v4l2_device_get(vdev->v4l2_dev);
#if defined(CONFIG_MEDIA_CONTROLLER)
/* Part 5: Register the entity. */
if (vdev->v4l2_dev && vdev->v4l2_dev->mdev &&
vdev->vfl_type != VFL_TYPE_SUBDEV) {
vdev->entity.type = MEDIA_ENT_T_DEVNODE_V4L;
vdev->entity.name = vdev->name;
vdev->entity.info.v4l.major = VIDEO_MAJOR;
vdev->entity.info.v4l.minor = vdev->minor;
ret = media_device_register_entity(vdev->v4l2_dev->mdev,
&vdev->entity);
if (ret < 0)
printk(KERN_WARNING
"%s: media_device_register_entity failed\n",
__func__);
}
#endif
/* Part 6: Activate this minor. The char device can now be used. */
set_bit(V4L2_FL_REGISTERED, &vdev->flags);
mutex_lock(&videodev_lock);
video_device[vdev->minor] = vdev;
mutex_unlock(&videodev_lock);
return 0;
cleanup:
mutex_lock(&videodev_lock);
if (vdev->cdev)
cdev_del(vdev->cdev);
devnode_clear(vdev);
mutex_unlock(&videodev_lock);
/* Mark this video device as never having been registered. */
vdev->minor = -1;
return ret;
}
decimator的注册跟clipper注册是一样的,将subdev添加到v4l2_device的子设备链表中,调用register_nxp_video注册一个video设备,生成/dev/video*结点。
sensor是被看成是subdev的,注册时调用了_register_sensor
static struct v4l2_subdev *_register_sensor(struct nxp_capture *me,
struct nxp_v4l2_i2c_board_info *board_info)
{
struct v4l2_subdev *sensor = NULL;
struct i2c_adapter *adapter;
struct media_entity *input;
u32 pad;
u32 flags;
int ret;
static int sensor_index = 0;
if (board_info && board_info->board_info) {
adapter = i2c_get_adapter(board_info->i2c_adapter_id);
if (!adapter) {
pr_err("%s: unable to get i2c adapter %d for device %s\n",
__func__,
board_info->i2c_adapter_id,
board_info->board_info->type);
return NULL;
}
sensor = v4l2_i2c_new_subdev_board(me->get_v4l2_device(me),
adapter, board_inf返回一个v4l2_subdevNULL); //返回一个v4l2_subdev
if (!sensor) {
pr_err("%s: unable to register subdev %s\n",
__func__, board_info->board_info->type);
return NULL;
}
} else {
if (external_sensor)
sensor = external_sensor;
else {
pr_err("%s: external sensor is NULL!!!\n", __func__);
return NULL;
}
}
sensor->host_priv = board_info;
if (me->interface_type == NXP_CAPTURE_INF_CSI) {
input = &me->csi.subdev.entity;
pad = NXP_CSI_PAD_SINK;
flags = 0;
} else {
input = &me->vin_clipper.subdev.entity;
pad = NXP_VIN_PAD_SINK;
flags = 0;
}
ret = media_entity //将sensor->entity与-csi.subdev.entity, pad, flags); //将sensor->entity与csi.subdev.entity连接起来
if (ret < 0) {
pr_err("%s: failed to media_entity_create_link()\n", __func__);
return NULL;
}
if (board_info && board_info->board_info) {
/* _set_sensor_entity_name(me->module, board_info->board_info->type, board_info->i2c_adapter_id, board_info->board_info->addr); */
_set_sensor_entity_name(sensor_index, board_info->board_info->type, board_info->i2c_adapter_id, board_info->board_info->addr);
} else {
// _set_sensor_entity_name(sensor_index, "loopback-sensor", 0, 0x00);
set_sensor_entity_name_without_i2c(sensor_index);
}
````````
sensor_index++;
return sensor; //返回sensor v4l2_subdev
}
3.5 scaler创建与注册
scaler用于旋转、翻转图片,scaler的创建与clipper、decimator的创建是一样的,比较特别的是scaler->subdev的entity与其vdev.entity的link的双向的,也就是说有两个link分别指向对方,说明数据可能会双向传输。
create_nxp_scaler调用的_init_entities函数中可以看到调用了media_entity_create_link函数两次,参数是一样的,位置调整了。
/* create link */
/* my source-> video sink */
ret = media_entity_create_link(entity, NXP_SCALER_PAD_SOURCE,
&me->video->vdev.entity, 0, 0);
if (ret < 0) {
pr_err("%s: failed to link my source to video sink\n", __func__);
goto error_link;
}
/* video source -> my sink */
ret = media_entity_create_link(&me->video->vdev.entity, 1,
entity, NXP_SCALER_PAD_SINK, 0);
if (ret < 0) {
pr_err("%s: failed to link video source to my sink\n", __func__);
goto error_link;
}
register_nxp_scaler函数与clipper、decimater的注册函数并没有太大区别,都将subdev添加到子设备链表中,注册一个video_device设备。
3.6 subdev注册
之前将所有的subdev都添加到了v4l2_device的子设备链表,接下来调用v4l2_device_register_subdev_nodes来注册这些子设备。
int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
{
struct video_device *vdev;
struct v4l2_subdev *sd;
int err;
/* Register a device node for every subdev marked with the
* V4L2_SUBDEV_FL_HAS_DEVNODE flag.
*/
list_for_each_entry(sd, &v4l2_dev->subdevs, list) { /* 遍历链表 */
if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
continue;
vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
if (!vdev) {
err = -ENOMEM;
goto clean_up;
}
video_set_drvdata(vdev, sd);
strlcpy(vdev->name, sd->name, sizeof(vdev->name));
vdev->v4l2_dev = v4l2_dev;
vdev->fops = &v4l2_subdev_fops;
vdev->release = v4l2_device_release_subdev_node;
vdev->ctrl_handler = sd->ctrl_handler;
err = __video_register_device(vdev, VFL_TYPE_SUBDEV, -1, 1,
sd->owner); /* 注册subdev,会生成/dev/v4l-subdev*结点 */
if (err < 0) {
kfree(vdev);
goto clean_up;
}
#if defined(CONFIG_MEDIA_CONTROLLER)
sd->entity.info.v4l.major = VIDEO_MAJOR;
sd->entity.info.v4l.minor = vdev->minor;
#endif
sd->devnode = vdev;
}
return 0;
clean_up:
list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
if (!sd->devnode)
break;
video_unregister_device(sd->devnode);
}
return err;
}
3.7 总结
nxp v4l2到注册到这里已经完成了。所有的subdev挂载到v4l2_device的子设备链表,entity都link起来了,当然,除了scaler->subdev->entity,下面是entity链接示意图
数据由sensor传输到csi,经过clipper完成修剪,传输到decimator完成缩放工作。
scaler在驱动注册时并链接到decimater,有可能这个工作是在HAL层中完成了,也有可能驱动层次不使用这个功能,图片的旋转、翻转由应用层实现。
图中所有的subdev都v4l2_device的子设备,都会被添加到其子设备链表中。
补充UML图及平台特有结构体