NXP

linux v4l2

2019-07-12 12:22发布

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 "); return -EINVAL; } nxp_v4l2 = kzalloc(sizeof(*nxp_v4l2), GFP_KERNEL); //分配nxp_v4l2内存 if (!nxp_v4l2) { pr_err("%s error: fail to kzalloc(size %d) ", __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 ", __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 ", __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 ", __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() ", __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() ", __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() ", __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() ", __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() ", __func__); goto err_create_out; } ret = register_nxp_out(nxp_v4l2->out); if (ret < 0) { pr_err("%s: failed to register_nxp_out() ", __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() ", __func__); goto err_register_out_subdev; } platform_set_drvdata(pdev, nxp_v4l2); printk("%s success ", __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!!! ", __func__); return ERR_PTR(-EINVAL); } /* * only support continuous memory */ if (flags & VB2ION_CTX_VMCONTIG) { pr_err("%s error: not support vmalloc ion context ", __func__); return ERR_PTR(-EINVAL); } ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); //分配ctx内存 if (!ctx) { pr_err("%s error: fail to kzalloc size: %d ", __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; }   媒体设备结点注册函数,这个函数主要完成三个工作
  1. 找到可用次设备号
  2. 注册字符设备
  3. 注册媒体设备(挂载到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 "); 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 ", __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 ", __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 ", __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 ", __func__); goto error_csi; } ret = nxp_csi_init(&me->csi, pdata->csi); /* csi初始化 */ if (ret < 0) { pr_err("%s: failed to nxp_csi_init() ", __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() ", __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() ", __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 ", __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 ", __func__); goto error_link; } #endif /* * sysfs */ create_sensor_sysfs(me->module); vmsg("%s exit(%p) ", __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() ", __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() ", __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() ", __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() ", __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() ", __func__); goto error_decimator; } #endif if (NULL == _register_sensor(me, me->platdata->sensor)) { pr_err("%s: can't register sensor subdev ", __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() ", __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 ", __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() ", __func__); return ret; } ret = register_nxp_video(me->video); /* 注册一个video设备,生成/dev/video*结点 */ if (ret < 0) { pr_err("%s: failed to register_nxp_video() ", __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 ", __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 "); 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 "); 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 ", __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 ", __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 ", __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 ", __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的,注册时调用了_re

热门文章