linux v4l2

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;
}

  媒体设备结点注册函数,这个函数主要完成三个工作

  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\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图及平台特有结构体

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值