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_t slock; struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; struct samsung_fimc_variant *variant; u16 id; u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; struct resource *regs_res; int irq; wait_queue_head_t irq_queue; struct fimc_m2m_device m2m; struct fimc_vid_cap vid_cap; unsigned long state; 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_1p input 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_t slock; struct fimc_frame s_frame; struct fimc_frame d_frame; u32 out_order_1p; u32 out_order_2p; u32 in_order_1p; u32 in_order_2p; enum fimc_datapath in_path; enum fimc_datapath out_path; struct fimc_scaler scaler; struct fimc_effect effect; int rotation; u32 flip; u32 flags; u32 state; struct fimc_dev *fimc_dev; struct v4l2_m2m_ctx *m2m_ctx; };
/* fimc controller abstration */ struct fimc_control { int id; /* controller id */ char name[16]; atomic_t in_use; void __iomem *regs; /* register i/o */ struct clk *clk; /* interface clock */ struct regulator *regulator; /* pd regulator */ struct fimc_meminfo mem; /* for reserved mem */ /* kernel helpers */ struct mutex lock; /* controller lock */ struct mutex alloc_lock; struct mutex v4l2_lock; wait_queue_head_t wq; struct device *dev; int irq; /* v4l2 related */ struct video_device *vd; struct v4l2_device v4l2_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_fbinfo fb; /* fimd info */ struct fimc_scaler sc; /* scaler info */ struct fimc_effect fe; /* fimc effect info */ enum fimc_status status; enum fimc_log log; u32 ctx_busy[FIMC_MAX_CTXS]; };
/* global */ struct fimc_global { struct fimc_control ctrl[FIMC_DEVICES]; struct s3c_platform_camera camera[FIMC_MAXCAMS]; int camera_isvalid[FIMC_MAXCAMS]; int active_camera; int initialized; };
设备对象结构:在platform 平台 probe时使用,传递给设备驱动
struct platform_device { const char * name; int id; struct device dev; u32 num_resources; struct resource * resource; const struct platform_device_id *id_entry; /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata; };
设备信息:
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_global fimc_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, },
static struct 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;
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;
主要是设置 其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; } #endif vdev->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 <[email protected]>"); 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_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;
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); else client = 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
分析:就是分析问题
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;很重要 #endif int 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; } } #endif spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); return 0;
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); else client = 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; #endif int 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; } } #endif spin_lock(&v4l2_dev->lock); list_add_tail(&sd->list, &v4l2_dev->subdevs); spin_unlock(&v4l2_dev->lock); return 0;