linux camera ---1

来源:互联网 发布:js点击按钮隐藏input 编辑:程序博客网 时间:2024/06/07 05:40

linux 摄像头驱动 :

核心数据结构:


/** * struct fimc_dev - abstraction for FIMC entity * @slock:the spinlock protecting this data structure * @lock:the mutex protecting this data structure * @pdev:pointer to the FIMC platform device * @pdata:pointer to the device platform data * @variant:the IP variant information * @id:FIMC device index (0..FIMC_MAX_DEVS) * @num_clocks: the number of clocks managed by this device instance * @clock:clocks required for FIMC operation * @regs:the mapped hardware registers * @regs_res:the resource claimed for IO registers * @irq:FIMC interrupt number * @irq_queue:interrupt handler waitqueue * @m2m:memory-to-memory V4L2 device information * @vid_cap:camera capture device information * @state:flags used to synchronize m2m and capture mode operation * @alloc_ctx:videobuf2 memory allocator context */struct fimc_dev {spinlock_tslock;struct mutexlock;struct platform_device*pdev;struct s5p_platform_fimc*pdata;struct samsung_fimc_variant*variant;u16id;u16num_clocks;struct clk*clock[MAX_FIMC_CLOCKS];void __iomem*regs;struct resource*regs_res;intirq;wait_queue_head_tirq_queue;struct fimc_m2m_devicem2m;struct fimc_vid_capvid_cap;unsigned longstate;struct vb2_alloc_ctx*alloc_ctx;};/** * fimc_ctx - the device context data * @slock:spinlock protecting this data structure * @s_frame:source frame properties * @d_frame:destination frame properties * @out_order_1p:output 1-plane YCBCR order * @out_order_2p:output 2-plane YCBCR order * @in_order_1pinput 1-plane YCBCR order * @in_order_2p:input 2-plane YCBCR order * @in_path:input mode (DMA or camera) * @out_path:output mode (DMA or FIFO) * @scaler:image scaler properties * @effect:image effect * @rotation:image clockwise rotation in degrees * @flip:image flip mode * @flags:additional flags for image conversion * @state:flags to keep track of user configuration * @fimc_dev:the FIMC device this context applies to * @m2m_ctx:memory-to-memory device context */struct fimc_ctx {spinlock_tslock;struct fimc_frames_frame;struct fimc_framed_frame;u32out_order_1p;u32out_order_2p;u32in_order_1p;u32in_order_2p;enum fimc_datapathin_path;enum fimc_datapathout_path;struct fimc_scalerscaler;struct fimc_effecteffect;introtation;u32flip;u32flags;u32state;struct fimc_dev*fimc_dev;struct v4l2_m2m_ctx*m2m_ctx;};

/* fimc controller abstration */struct fimc_control {intid;/* controller id */charname[16];atomic_tin_use;void __iomem*regs;/* register i/o */struct clk*clk;/* interface clock */struct regulator*regulator;/* pd regulator */struct fimc_meminfomem;/* for reserved mem *//* kernel helpers */struct mutexlock;/* controller lock */struct mutexalloc_lock;struct mutexv4l2_lock;wait_queue_head_twq;struct device*dev;intirq;/* v4l2 related */struct video_device*vd;struct v4l2_devicev4l2_dev;/* fimc specific */struct fimc_limit*limit;/* H/W limitation */struct s3c_platform_camera*cam;/* activated camera */struct fimc_capinfo*cap;/* capture dev info */struct fimc_outinfo*out;/* output dev info */struct fimc_fbinfofb;/* fimd info */struct fimc_scalersc;/* scaler info */struct fimc_effectfe;/* fimc effect info */enum fimc_statusstatus;enum fimc_loglog;u32ctx_busy[FIMC_MAX_CTXS];};


/* global */struct fimc_global {struct fimc_controlctrl[FIMC_DEVICES];struct s3c_platform_cameracamera[FIMC_MAXCAMS];intcamera_isvalid[FIMC_MAXCAMS];intactive_camera;intinitialized;};




设备对象结构:在platform 平台 probe时使用,传递给设备驱动

struct platform_device {const char* name;intid;struct devicedev;u32num_resources;struct resource* resource;const struct platform_device_id*id_entry;/* MFD cell pointer */struct mfd_cell *mfd_cell;/* arch specific additions */struct pdev_archdataarchdata;};

设备信息:

static struct i2c_board_info  ov9650_i2c_info = {I2C_BOARD_INFO("OV9650", 0x60>>1),.platform_data = &ov9650_plat,};static struct s3c_platform_camera ov9650 = {.id= CAMERA_PAR_A,.type= CAM_TYPE_ITU,.fmt= ITU_601_YCBCR422_8BIT,.order422= CAM_ORDER422_8BIT_CBYCRY,.i2c_busnum= IIC_NUM_CAM_USED,.info= &ov9650_i2c_info,.pixelformat= V4L2_PIX_FMT_VYUY,.srclk_name= "mout_mpll",.clk_name= "sclk_cam0",.clk_rate= 24000000,//.line_length= 640,.line_length= 1920,.width= 640,.height= 480,.window= {.left= 0,.top= 0,.width= 640,.height= 480,},/* Polarity */.inv_pclk= 0,.inv_vsync= 0,.inv_href= 0,.inv_hsync= 0,.initialized= 0,//.cam_power= smdkv210_OV9650_power,.cam_power= tqcam_OV9650_power,};


static struct s3c_platform_fimc fimc_plat_lsi = {.srclk_name= "mout_mpll",.clk_name= "sclk_fimc",.lclk_name= "fimc",.clk_rate= 166750000,#if defined(CONFIG_VIDEO_S5K4EA).default_cam= CAMERA_CSI_C,#else#ifdef CAM_ITU_CH_A.default_cam= CAMERA_PAR_A,#else.default_cam= CAMERA_PAR_B,#endif#endif.camera= {&ov9650,},.hw_ver= 0x43,};

static struct s5p_media_device tq210_media_devs[] = {[0] = {.id= S5P_MDEV_MFC,.name= "mfc",.bank= 0,.memsize= S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0,.paddr= 0,},[1] = {.id= S5P_MDEV_MFC,.name= "mfc",.bank= 1,.memsize= S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1,.paddr= 0,},[2] = {.id= S5P_MDEV_FIMC0,.name= "fimc0",.bank= 1,.memsize= S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0,.paddr= 0,},.............................
media_devs指向tq210_media_devs数组;


void __init s3c_fimc0_set_platdata(struct s3c_platform_fimc *pd){struct s3c_platform_fimc *npd;if (!pd)pd = &default_fimc0_data;npd = kmemdup(pd, sizeof(struct s3c_platform_fimc), GFP_KERNEL);if (!npd)printk(KERN_ERR "%s: no memory for platform data\n", __func__);else {if (!npd->cfg_gpio)npd->cfg_gpio = s3c_fimc0_cfg_gpio;if (!npd->clk_on)npd->clk_on = s3c_fimc_clk_on;if (!npd->clk_off)npd->clk_off = s3c_fimc_clk_off;npd->hw_ver = 0x45;/* starting physical address of memory region */npd->pmem_start = s5p_get_media_memory_bank(S5P_MDEV_FIMC0, 1);/* size of memory region */npd->pmem_size = s5p_get_media_memsize_bank(S5P_MDEV_FIMC0, 1);s3c_device_fimc0.dev.platform_data = npd;}

struct platform_device s3c_device_fimc0 = {.name= "s3c-fimc",.id= 0,.num_resources= ARRAY_SIZE(s3c_fimc0_resource),.resource= s3c_fimc0_resource,};



设备驱动注册:入口处;

static struct platform_driver fimc_driver = {.probe= fimc_probe,.remove= fimc_remove,.suspend= fimc_suspend,.resume= fimc_resume,.driver= {.name= FIMC_NAME,.owner= THIS_MODULE,},};

static int fimc_register(void){platform_driver_register(&fimc_driver);return 0;}static void fimc_unregister(void){platform_driver_unregister(&fimc_driver);}late_initcall(fimc_register);

在注册fimc_driver驱动后;根据bus device driver 在platform平台上匹配;会调用

fimc_driver->fimc_probe();函数;注册绑定设备对象;

static int __devinit fimc_probe(struct platform_device *pdev){struct s3c_platform_fimc *pdata;struct fimc_control *ctrl;struct clk *srclk;int ret;if (!fimc_dev) {   struct *fimc_globalfimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);if (!fimc_dev) {dev_err(&pdev->dev, "%s: not enough memory\n",__func__);return -ENOMEM;}}ctrl = fimc_register_controller(pdev);                 if (!ctrl) {printk(KERN_ERR "%s: cannot register fimc\n", __func__);goto err_alloc;}pdata = to_fimc_plat(&pdev->dev);if (pdata->cfg_gpio)pdata->cfg_gpio(pdev);/* fimc source clock */srclk = clk_get(&pdev->dev, pdata->srclk_name);/* fimc clock */ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);/* set parent for mclk */clk_set_parent(ctrl->clk, srclk);/* set rate for mclk */clk_set_rate(ctrl->clk, pdata->clk_rate);/* V4L2 device-subdev registration */ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);if (ret) {fimc_err("%s: v4l2 device register failed\n", __func__);goto err_fimc;}/* things to initialize once */if (!fimc_dev->initialized) {ret = fimc_init_global(pdev);if (ret)goto err_v4l2;}/* video device register */ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);if (ret) {fimc_err("%s: cannot register video driver\n", __func__);goto err_v4l2;}video_set_drvdata(ctrl->vd, ctrl);ret = device_create_file(&(pdev->dev), &dev_attr_log_level);..............return -EINVAL;


对于设备信息的传递:

根据platform_device的id

ctrl->vd = &fimc_video_device[id];

指向:

struct video_device fimc_video_device[FIMC_DEVICES] = {[0] = {.fops = &fimc_fops,.ioctl_ops = &fimc_v4l2_ops,.release = fimc_vdev_release,},

staticstruct fimc_control *fimc_register_controller(struct platform_device *pdev){struct s3c_platform_fimc *pdata;struct fimc_control *ctrl;struct resource *res;int id, mdev_id;id = pdev->id;mdev_id = S5P_MDEV_FIMC0 + id;pdata = to_fimc_plat(&pdev->dev);ctrl = get_fimc_ctrl(id);ctrl->id = id;ctrl->dev = &pdev->dev;ctrl->vd = &fimc_video_device[id];ctrl->vd->minor = id;/* alloc from bank1 as default */ctrl->mem.base = pdata->pmem_start;ctrl->mem.size = pdata->pmem_size;ctrl->mem.curr = ctrl->mem.base;ctrl->status = FIMC_STREAMOFF;switch (pdata->hw_ver) {case 0x40:ctrl->limit = &fimc40_limits[id];break;..........................}ctrl->log = FIMC_LOG_DEFAULT;sprintf(ctrl->name, "%s%d", FIMC_NAME, id);strcpy(ctrl->vd->name, ctrl->name);atomic_set(&ctrl->in_use, 0);mutex_init(&ctrl->lock);mutex_init(&ctrl->alloc_lock);mutex_init(&ctrl->v4l2_lock);init_waitqueue_head(&ctrl->wq);/* get resource for io memory */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {fimc_err("%s: failed to get io memory region\n", __func__);return NULL;}/* request mem region request_mem_region函数并没有做实际性的映射工作,只是告诉内核要使用一块内存地址,声明占有,也方便内核管理这些资源。*/res = request_mem_region(res->start, res->end - res->start + 1,pdev->name);if (!res) {fimc_err("%s: failed to request io memory region\n", __func__);return NULL;}/* ioremap for register block 在将I/O内存资源的物理地址映射成核心虚地址后 */ctrl->regs = ioremap(res->start, res->end - res->start + 1);if (!ctrl->regs) {fimc_err("%s: failed to remap io region\n", __func__);return NULL;}/* irq */ctrl->irq = platform_get_irq(pdev, 0);if (request_irq(ctrl->irq, fimc_irq, IRQF_DISABLED, ctrl->name, ctrl))fimc_err("%s: request_irq failed\n", __func__);fimc_hwset_reset(ctrl);return ctrl;
v4l2_device和device关联起来
int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev){if (v4l2_dev == NULL)return -EINVAL;INIT_LIST_HEAD(&v4l2_dev->subdevs);spin_lock_init(&v4l2_dev->lock);mutex_init(&v4l2_dev->ioctl_lock);v4l2_prio_init(&v4l2_dev->prio);kref_init(&v4l2_dev->ref);v4l2_dev->dev = dev;if (dev == NULL) {/* If dev == NULL, then name must be filled in by the caller */WARN_ON(!v4l2_dev->name[0]);return 0;}/* Set name to driver name + device name if it is empty. */if (!v4l2_dev->name[0])snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",dev->driver->name, dev_name(dev));if (!dev_get_drvdata(dev))dev_set_drvdata(dev, v4l2_dev);return 0;

获取platform_device的相关信息

static int fimc_init_global(struct platform_device *pdev){struct s3c_platform_fimc *pdata;struct s3c_platform_camera *cam;int i;pdata = to_fimc_plat(&pdev->dev);/* Registering external camera modules. re-arrange order to be sure */for (i = 0; i < FIMC_MAXCAMS; i++) {cam = pdata->camera[i];if (!cam)break;cam->srclk = clk_get(&pdev->dev, cam->srclk_name);if (IS_ERR(cam->srclk)) {dev_err(&pdev->dev, "%s: failed to get mclk source\n",__func__);return -EINVAL;}/* mclk */cam->clk = clk_get(&pdev->dev, cam->clk_name);if (IS_ERR(cam->clk)) {dev_err(&pdev->dev, "%s: failed to get mclk source\n",__func__);clk_put(cam->srclk);return -EINVAL;}clk_put(cam->clk);clk_put(cam->srclk);/* Assign camera device to fimc */memcpy(&fimc_dev->camera[i], cam, sizeof(*cam));fimc_dev->camera_isvalid[i] = 1;fimc_dev->camera[i].initialized = 0;}fimc_dev->active_camera = -1;fimc_dev->initialized = 1;return 0;

对于:video_register_device

主要是设置 其cdev的fops

vdev->cdev->ops = &v4l2_fops;

并注册

cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);;

设置设备号

vdev->dev.devt = MKDEV(VIDEO_MAJOR, vdev->minor);

注册device到总线中去;

ret = device_register(&vdev->dev);

vdev->dev.release = v4l2_device_release;

注册entry:

ret = media_device_register_entity(vdev->v4l2_dev->mdev,。。。

io口操作时要用:

video_device[vdev->minor] = vdev;

/** *__video_register_device - register video4linux devices *@vdev: video device structure we want to register *@type: type of device to register *@nr:   which device node number (0 == /dev/video0, 1 == /dev/video1, ... *             -1 == first free) *@warn_if_nr_in_use: warn if the desired device node number *       was already in use and another number was chosen instead. *@owner: module that owns the video device node * *The registration code assigns minor numbers and device node numbers *based on the requested type and registers the new device node with *the kernel. * *This function assumes that struct video_device was zeroed when it *was allocated and does not contain any stale date. * *An error is returned if no free minor or device node number could be *found, or if the registration of the device node failed. * *Zero is returned on success. * *Valid types are * *%VFL_TYPE_GRABBER - A frame grabber * *%VFL_TYPE_VBI - Vertical blank data (undecoded) * *%VFL_TYPE_RADIO - A radio card * *%VFL_TYPE_SUBDEV - A subdevice */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 */WARN_ON(!vdev->release);if (!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";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;}#endifvdev->minor = i + minor_offset;vdev->num = nr;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.v4l.major = VIDEO_MAJOR;vdev->entity.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;}EXPORT_SYMBOL(__video_register_device);/** *video_unregister_device - unregister a video4linux device *@vdev: the device to unregister * *This unregisters the passed device. Future open calls will *be met with errors. */void video_unregister_device(struct video_device *vdev){/* Check if vdev was ever registered at all */if (!vdev || !video_is_registered(vdev))return;mutex_lock(&videodev_lock);/* This must be in a critical section to prevent a race with v4l2_open. * Once this bit has been cleared video_get may never be called again. */clear_bit(V4L2_FL_REGISTERED, &vdev->flags);mutex_unlock(&videodev_lock);device_unregister(&vdev->dev);}EXPORT_SYMBOL(video_unregister_device);/* *Initialise video for linux */static int __init videodev_init(void){dev_t dev = MKDEV(VIDEO_MAJOR, 0);int ret;printk(KERN_INFO "Linux video capture interface: v2.00\n");ret = register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);if (ret < 0) {printk(KERN_WARNING "videodev: unable to get major %d\n",VIDEO_MAJOR);return ret;}ret = class_register(&video_class);if (ret < 0) {unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);printk(KERN_WARNING "video_dev: class_register failed\n");return -EIO;}return 0;}static void __exit videodev_exit(void){dev_t dev = MKDEV(VIDEO_MAJOR, 0);class_unregister(&video_class);unregister_chrdev_region(dev, VIDEO_NUM_DEVICES);}module_init(videodev_init)module_exit(videodev_exit)MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");MODULE_LICENSE("GPL");MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);/* * Local variables: * c-basic-offset: 8 * End: */
设置drv_privedata;

video_set_drvdata(ctrl->vd, ctrl);

    if(ctrl->vd->dev->p->driver_data ==null)
     即ctrl->vd->dev->p->driver_data = ctrl;

对于v4l2_subdev的注册即iic设备的注册; 以及和v4l2设备的绑定如何??

static const struct i2c_device_id ov3640_id[] = {{ OV3640_DRIVER_NAME, 0 },{ },};MODULE_DEVICE_TABLE(i2c, ov3640_id);static struct v4l2_i2c_driver_data v4l2_i2c_data = {.name = OV3640_DRIVER_NAME,.probe = ov3640_probe,.remove = __devexit_p(ov3640_remove),.id_table = ov3640_id,};

/* Bus-based I2C implementation for kernels >= 2.6.26 */static int __init v4l2_i2c_drv_init(void){v4l2_i2c_driver.driver.name = v4l2_i2c_data.name;v4l2_i2c_driver.command = v4l2_i2c_data.command;v4l2_i2c_driver.probe = v4l2_i2c_data.probe;v4l2_i2c_driver.remove = v4l2_i2c_data.remove;v4l2_i2c_driver.suspend = v4l2_i2c_data.suspend;v4l2_i2c_driver.resume = v4l2_i2c_data.resume;v4l2_i2c_driver.id_table = v4l2_i2c_data.id_table;return i2c_add_driver(&v4l2_i2c_driver);}static void __exit v4l2_i2c_drv_cleanup(void){i2c_del_driver(&v4l2_i2c_driver);}module_init(v4l2_i2c_drv_init);module_exit(v4l2_i2c_drv_cleanup);


关于iic的注册分析iic设备去分析iic设备驱动;
如果匹配到

static const struct v4l2_subdev_core_ops ov3640_core_ops = {.init = ov3640_init,/* initializing API */.s_config = ov3640_s_config,/* Fetch platform data */.queryctrl = ov3640_queryctrl,.querymenu = ov3640_querymenu,.g_ctrl = ov3640_g_ctrl,.s_ctrl = ov3640_s_ctrl,};static const struct v4l2_subdev_video_ops ov3640_video_ops = {.g_fmt = ov3640_g_fmt,.s_fmt = ov3640_s_fmt,.enum_framesizes = ov3640_enum_framesizes,.enum_frameintervals = ov3640_enum_frameintervals,.enum_fmt = ov3640_enum_fmt,.try_fmt = ov3640_try_fmt,.g_parm = ov3640_g_parm,.s_parm = ov3640_s_parm,};static const struct v4l2_subdev_ops ov3640_ops = {.core = &ov3640_core_ops,.video = &ov3640_video_ops,};static int ov3640_remove(struct i2c_client *client);/* * ov3640_probe * Fetching platform data is being done with s_config subdev call. * In probe routine, we just register subdev device */static int ov3640_probe(struct i2c_client *client, const struct i2c_device_id *id){struct ov3640_state *state;struct v4l2_subdev *sd;int err = 0;state = kzalloc(sizeof(struct ov3640_state), GFP_KERNEL);if (state == NULL)return -ENOMEM;sd = &state->sd;strcpy(sd->name, OV3640_DRIVER_NAME);/* Registering subdev */v4l2_i2c_subdev_init(sd, client, &ov3640_ops);err =  checkIfOV3640(sd);dev_info(&client->dev, "ov3640 has been probed,err(%d)\n",err);if(err < 0)ov3640_remove(client);return err;}

设置client和sd;

void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,const struct v4l2_subdev_ops *ops){v4l2_subdev_init(sd, ops);sd->flags |= V4L2_SUBDEV_FL_IS_I2C;/* the owner is the same as the i2c_client's driver owner */sd->owner = client->driver->driver.owner;/* i2c_client and v4l2_subdev point to one another */v4l2_set_subdevdata(sd, client);i2c_set_clientdata(client, sd);/* initialize name */snprintf(sd->name, sizeof(sd->name), "%s %d-%04x",client->driver->driver.name, i2c_adapter_id(client->adapter),client->addr);}



其中v4l2_subdev和i2c_client相互关联并设置其ov3640_ops;

而在  v4l2_device中设置input  camera id

 此处分析问题

int fimc_s_input(struct file *file, void *fh, unsigned int i){struct fimc_global *fimc = get_fimc_dev();struct fimc_control *ctrl = ((struct fimc_prv_data *)fh)->ctrl;int ret = 0;fimc_dbg("%s: index %d\n", __func__, i);if (i < 0 || i >= FIMC_MAXCAMS) {fimc_err("%s: invalid input index\n", __func__);return -EINVAL;}if (!fimc->camera_isvalid[i])return -EINVAL;if (fimc->camera[i].sd && ctrl->id != 2) {fimc_err("%s: Camera already in use.\n", __func__);return -EBUSY;}mutex_lock(&ctrl->v4l2_lock);/* If ctrl->cam is not NULL, there is one subdev already registered. * We need to unregister that subdev first. */if (i != fimc->active_camera) {fimc_release_subdev(ctrl);ctrl->cam = &fimc->camera[i];ret = fimc_configure_subdev(ctrl);if (ret < 0) {mutex_unlock(&ctrl->v4l2_lock);fimc_err("%s: Could not register camera sensor ""with V4L2.\n", __func__);return -ENODEV;}fimc->active_camera = i;}if (ctrl->id == 2) {if (i == fimc->active_camera) {ctrl->cam = &fimc->camera[i];} else {mutex_unlock(&ctrl->v4l2_lock);return -EINVAL;}}mutex_unlock(&ctrl->v4l2_lock);return 0;

其中配置fimc_configure_subdev();为核心

int fimc_configure_subdev(struct fimc_control *ctrl){struct i2c_adapter *i2c_adap;struct i2c_board_info *i2c_info;struct v4l2_subdev *sd;unsigned short addr;int err;char *name;err = 0;/* set parent for mclk */if (clk_get_parent(ctrl->cam->clk->parent))clk_set_parent(ctrl->cam->clk->parent, ctrl->cam->srclk);/* set rate for mclk */if (clk_get_rate(ctrl->cam->clk))clk_set_rate(ctrl->cam->clk, ctrl->cam->clk_rate);i2c_adap = i2c_get_adapter(ctrl->cam->i2c_busnum);if (!i2c_adap)fimc_err("subdev i2c_adapter missing-skip registration\n");i2c_info = ctrl->cam->info;if (!i2c_info) {fimc_err("%s: subdev i2c board info missing\n", __func__);return -ENODEV;}name = i2c_info->type;if (!name) {fimc_err("subdev i2c driver name missing-skip registration\n");return -ENODEV;}addr = i2c_info->addr;if (!addr) {fimc_err("subdev i2c address missing-skip registration\n");return -ENODEV;}/* * NOTE: first time subdev being registered, * s_config is called and try to initialize subdev device * but in this point, we are not giving MCLK and power to subdev * so nothing happens but pass platform data through */sd = v4l2_i2c_new_subdev_board(&ctrl->v4l2_dev, i2c_adap,i2c_info, &addr);if (!sd) {fimc_err("%s: v4l2 subdev board registering failed\n",__func__);err = -1;}/* Assign subdev to proper camera device pointer */ctrl->cam->sd = sd;return err;//return 0;}
找到adapter;
struct i2c_adapter *i2c_get_adapter(int nr){struct i2c_adapter *adapter;mutex_lock(&core_lock);adapter = idr_find(&i2c_adapter_idr, nr);if (adapter && !try_module_get(adapter->owner))adapter = NULL;mutex_unlock(&core_lock);return adapter;}
i2c_info = ctrl->cam->info;addr = i2c_info->addr; camera的iic信息;


/* Load an i2c sub-device. */struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,struct i2c_adapter *adapter, struct i2c_board_info *info,const unsigned short *probe_addrs){struct v4l2_subdev *sd = NULL;struct i2c_client *client;BUG_ON(!v4l2_dev);request_module(I2C_MODULE_PREFIX "%s", info->type);/* Create the i2c client */if (info->addr == 0 && probe_addrs)client = i2c_new_probed_device(adapter, info, probe_addrs,       NULL);elseclient = i2c_new_device(adapter, info); /* Note: by loading the module first we are certain that c->driver   will be set if the driver was found. If the module was not loaded   first, then the i2c core tries to delay-load the module for us,   and then c->driver is still NULL until the module is finally   loaded. This delay-load mechanism doesn't work if other drivers   want to use the i2c device, so explicitly loading the module   is the best alternative. */if (client == NULL || client->driver == NULL)goto error;/* Lock the module so we can safely get the v4l2_subdev pointer */if (!try_module_get(client->driver->driver.owner))goto error;sd = i2c_get_clientdata(client);/* Register with the v4l2_device which increases the module's   use count as well. */if (v4l2_device_register_subdev(v4l2_dev, sd))sd = NULL;/* Decrease the module use count to match the first try_module_get. */module_put(client->driver->driver.owner);error:/* If we have a client but no subdev, then something went wrong and   we must unregister the client. */if (client && sd == NULL)i2c_unregister_device(client);return sd;}E

struct i2c_client * client = i2c_new_device(adapter, info);

分析:就是分析问题

int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,struct v4l2_subdev *sd){#if defined(CONFIG_MEDIA_CONTROLLER)struct media_entity *entity = &sd->entity;很重要#endifint err;/* Check for valid input */if (v4l2_dev == NULL || sd == NULL || !sd->name[0])return -EINVAL;/* Warn if we apparently re-register a subdev */WARN_ON(sd->v4l2_dev != NULL);if (!try_module_get(sd->owner))return -ENODEV;sd->v4l2_dev = v4l2_dev;if (sd->internal_ops && sd->internal_ops->registered) {err = sd->internal_ops->registered(sd);if (err) {module_put(sd->owner);return err;}}/* This just returns 0 if either of the two args is NULL */err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);if (err) {if (sd->internal_ops && sd->internal_ops->unregistered)sd->internal_ops->unregistered(sd);module_put(sd->owner);return err;}#if defined(CONFIG_MEDIA_CONTROLLER)/* Register the entity. */if (v4l2_dev->mdev) {err = media_device_register_entity(v4l2_dev->mdev, entity);if (err < 0) {if (sd->internal_ops && sd->internal_ops->unregistered)sd->internal_ops->unregistered(sd);module_put(sd->owner);return err;}}#endifspin_lock(&v4l2_dev->lock);list_add_tail(&sd->list, &v4l2_dev->subdevs);spin_unlock(&v4l2_dev->lock);return 0;

struct media_entity *entity = &sd->entity;  入口点

sd->v4l2_dev = v4l2_dev;

media_device_register_entity(v4l2_dev->mdev, entity);

         -----.>加入链表

list_add_tail(&sd->list, &v4l2_dev->subdevs);

ctrl->cam->sd = sd;


static inline int fimc_mmap_cap(struct file *filp, struct vm_area_struct *vma)
{
struct fimc_prv_data *prv_data =
(struct fimc_prv_data *)filp->private_data;
struct fimc_control *ctrl = prv_data->ctrl;
u32 size = vma->vm_end - vma->vm_start;
u32 pfn, idx = vma->vm_pgoff;


vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_flags |= VM_RESERVED;


/*
* page frame number of the address for a source frame
* to be stored at.
*/
pfn = __phys_to_pfn(ctrl->cap->bufs[idx].base[0]);


if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
fimc_err("%s: writable mapping must be shared\n", __func__);
return -EINVAL;
}


if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
fimc_err("%s: mmap fail\n", __func__);
return -EINVAL;
}


return 0;
}


对于:v4l2_device和v4l2_sud关联;

这是另一种 v4l2_file_operations  fimc_capture_fops;

于一开始的v4l2_file_operations   fimc_ops实现不同但是 原理差不多;

static const struct v4l2_file_operations fimc_capture_fops = {
.owner = THIS_MODULE,
.open = fimc_capture_open,
.release = fimc_capture_close,
.poll = fimc_capture_poll,
.unlocked_ioctl= video_ioctl2,
.mmap = fimc_capture_mmap,
};

static int fimc_capture_open(struct file *file){struct fimc_dev *fimc = video_drvdata(file);int ret = 0;dbg("pid: %d, state: 0x%lx", task_pid_nr(current), fimc->state);/* Return if the corresponding video mem2mem node is already opened. */if (fimc_m2m_active(fimc))return -EBUSY;if (++fimc->vid_cap.refcnt == 1) {ret = fimc_isp_subdev_init(fimc, 0);if (ret) {fimc->vid_cap.refcnt--;return -EIO;}}file->private_data = fimc->vid_cap.ctx;return 0;

static int fimc_isp_subdev_init(struct fimc_dev *fimc, unsigned int index){struct s5p_fimc_isp_info *isp_info;struct s5p_platform_fimc *pdata = fimc->pdata;int ret;if (index >= pdata->num_clients)return -EINVAL;isp_info = &pdata->isp_info[index];if (isp_info->clk_frequency)clk_set_rate(fimc->clock[CLK_CAM], isp_info->clk_frequency);ret = clk_enable(fimc->clock[CLK_CAM]);if (ret)return ret;ret = fimc_subdev_attach(fimc, index);if (ret)return ret;ret = fimc_hw_set_camera_polarity(fimc, isp_info);if (ret)return ret;ret = v4l2_subdev_call(fimc->vid_cap.sd, core, s_power, 1);if (!ret)return ret;/* enabling power failed so unregister subdev */fimc_subdev_unregister(fimc);v4l2_err(&fimc->vid_cap.v4l2_dev, "ISP initialization failed: %d\n", ret);return ret;}

/** * fimc_subdev_attach - attach v4l2_subdev to camera host interface * * @fimc: FIMC device information * @index: index to the array of available subdevices, *   -1 for full array search or non negative value *   to select specific subdevice */static int fimc_subdev_attach(struct fimc_dev *fimc, int index){struct fimc_vid_cap *vid_cap = &fimc->vid_cap;struct s5p_platform_fimc *pdata = fimc->pdata;struct s5p_fimc_isp_info *isp_info;struct v4l2_subdev *sd;int i;for (i = 0; i < pdata->num_clients; ++i) {isp_info = &pdata->isp_info[i];if (index >= 0 && i != index)continue;sd = fimc_subdev_register(fimc, isp_info);if (!IS_ERR_OR_NULL(sd)) {vid_cap->sd = sd;vid_cap->input_index = i;return 0;}}vid_cap->input_index = -1;vid_cap->sd = NULL;v4l2_err(&vid_cap->v4l2_dev, "fimc%d: sensor attach failed\n", fimc->id);return -ENODEV;

static struct v4l2_subdev *fimc_subdev_register(struct fimc_dev *fimc,    struct s5p_fimc_isp_info *isp_info){struct i2c_adapter *i2c_adap;struct fimc_vid_cap *vid_cap = &fimc->vid_cap;struct v4l2_subdev *sd = NULL;i2c_adap = i2c_get_adapter(isp_info->i2c_bus_num);if (!i2c_adap)return ERR_PTR(-ENOMEM);sd = v4l2_i2c_new_subdev_board(&vid_cap->v4l2_dev, i2c_adap,       isp_info->board_info, NULL);if (!sd) {v4l2_err(&vid_cap->v4l2_dev, "failed to acquire subdev\n");return NULL;}v4l2_info(&vid_cap->v4l2_dev, "subdevice %s registered successfuly\n",isp_info->board_info->type);return sd;}


/* Load an i2c sub-device. */struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev,struct i2c_adapter *adapter, struct i2c_board_info *info,const unsigned short *probe_addrs){struct v4l2_subdev *sd = NULL;struct i2c_client *client;BUG_ON(!v4l2_dev);request_module(I2C_MODULE_PREFIX "%s", info->type);/* Create the i2c client */if (info->addr == 0 && probe_addrs)client = i2c_new_probed_device(adapter, info, probe_addrs,       NULL);elseclient = i2c_new_device(adapter, info);/* Note: by loading the module first we are certain that c->driver   will be set if the driver was found. If the module was not loaded   first, then the i2c core tries to delay-load the module for us,   and then c->driver is still NULL until the module is finally   loaded. This delay-load mechanism doesn't work if other drivers   want to use the i2c device, so explicitly loading the module   is the best alternative. */if (client == NULL || client->driver == NULL)goto error;/* Lock the module so we can safely get the v4l2_subdev pointer */if (!try_module_get(client->driver->driver.owner))goto error;sd = i2c_get_clientdata(client);/* Register with the v4l2_device which increases the module's   use count as well. */if (v4l2_device_register_subdev(v4l2_dev, sd))sd = NULL;/* Decrease the module use count to match the first try_module_get. */module_put(client->driver->driver.owner);error:/* If we have a client but no subdev, then something went wrong and   we must unregister the client. */if (client && sd == NULL)i2c_unregister_device(client);return sd;

EXPORT_SYMBOL_GPL(v4l2_device_unregister);int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,struct v4l2_subdev *sd){#if defined(CONFIG_MEDIA_CONTROLLER)struct media_entity *entity = &sd->entity;#endifint err;/* Check for valid input */if (v4l2_dev == NULL || sd == NULL || !sd->name[0])return -EINVAL;/* Warn if we apparently re-register a subdev */WARN_ON(sd->v4l2_dev != NULL);if (!try_module_get(sd->owner))return -ENODEV;sd->v4l2_dev = v4l2_dev;if (sd->internal_ops && sd->internal_ops->registered) {err = sd->internal_ops->registered(sd);if (err) {module_put(sd->owner);return err;}}/* This just returns 0 if either of the two args is NULL */err = v4l2_ctrl_add_handler(v4l2_dev->ctrl_handler, sd->ctrl_handler);if (err) {if (sd->internal_ops && sd->internal_ops->unregistered)sd->internal_ops->unregistered(sd);module_put(sd->owner);return err;}#if defined(CONFIG_MEDIA_CONTROLLER)/* Register the entity. */if (v4l2_dev->mdev) {err = media_device_register_entity(v4l2_dev->mdev, entity);if (err < 0) {if (sd->internal_ops && sd->internal_ops->unregistered)sd->internal_ops->unregistered(sd);module_put(sd->owner);return err;}}#endifspin_lock(&v4l2_dev->lock);list_add_tail(&sd->list, &v4l2_dev->subdevs);spin_unlock(&v4l2_dev->lock);return 0;


0 0