OK6410 V4L2 分析
阿新 • • 發佈:2018-12-10
/* 由mach-smdk6410.c檔案可知,核心啟動時將所有platform_device 包括 s3c_device_fimc0 s3c_device_fimc1 掛載到platform匯流排。 */ /* dev-fimc0.c */ struct platform_device s3c_device_fimc0 = { .name = "s3c-fimc", .id = 0, .num_resources = ARRAY_SIZE(s3c_fimc0_resource), .resource = s3c_fimc0_resource, }; /* dev-fimc1.c */ struct platform_device s3c_device_fimc1 = { .name = "s3c-fimc", .id = 1, .num_resources = ARRAY_SIZE(s3c_fimc1_resource), .resource = s3c_fimc1_resource, }; /* mach-smdk6410.c */ static struct platform_device *smdk6410_devices[] __initdata = { &s3c_device_hsmmc0, &s3c_device_hsmmc1, &s3c_device_i2c0, &s3c_device_fb, &s3c_device_ohci, &s3c_device_usb_hsotg, &samsung_asoc_dma, &samsung_device_keypad, &gpio_button_device, &s3c_device_nand, #ifdef CONFIG_DM9000 &s3c_device_dm9000, #endif &s3c64xx_device_ac97, &s3c_device_rtc, &s3c_device_ts, &s3c_device_fimc0,//FIMC 裝置 &s3c_device_fimc1, &s3c_device_wdt, &s3c_device_vpp, &s3c_device_mfc, &s3c_device_tvenc, &s3c_device_tvscaler, &s3c_device_rotator, &s3c_device_jpeg, &s3c_device_g2d, &s3c_device_g3d, &s3c64xx_device_spi0, &s3c64xx_device_spi1, }; platform_add_devices(smdk6410_devices, ARRAY_SIZE(smdk6410_devices)); /* platform匯流排的match函式將device和driver匹配之後, 會自動呼叫s3c_fimc_driver中指定的probe函式探測裝置、 申請記憶體資源、申請中斷等, 並將最終形成的platform_device型別資料儲存到核心中, 供後續使用。 */ /* s3c_fimc_core.c */ static int s3c_fimc_probe(struct platform_device *pdev) { struct s3c_platform_fimc *pdata; struct s3c_fimc_control *ctrl; struct clk *srclk; int ret; ctrl = s3c_fimc_register_controller(pdev); if (!ctrl) { err("cannot register fimc controller\n"); goto err_fimc; } 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); if (IS_ERR(srclk)) { err("failed to get source clock of fimc\n"); goto err_clk_io; } /* fimc clock */ ctrl->clock = clk_get(&pdev->dev, pdata->clk_name); if (IS_ERR(ctrl->clock)) { err("failed to get fimc clock source\n"); goto err_clk_io; } /* set parent clock */ clk_enable(ctrl->clock); /* things to initialize once */ if (ctrl->id == 0) { ret = s3c_fimc_init_global(pdev); if (ret) goto err_global; } /* 註冊 video4linux devices */ ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id); if (ret) { err("cannot register video driver\n"); goto err_video; } info("controller %d registered successfully\n", ctrl->id); return 0; err_video: clk_put(s3c_fimc.cam_clock); err_global: clk_disable(ctrl->clock); clk_put(ctrl->clock); err_clk_io: s3c_fimc_unregister_controller(pdev); err_fimc: return -EINVAL; } static struct platform_driver s3c_fimc_driver = { .probe = s3c_fimc_probe, .remove = s3c_fimc_remove, .suspend = s3c_fimc_suspend, .resume = s3c_fimc_resume, .driver = { .name = "s3c-fimc", .owner = THIS_MODULE, }, }; /* video_device結構體包括fops、ioctl_ops、release、name、vf_type幾個成員變數, 其中,最重要的是file_operations型別的[fops] 和 v4l2_ioctl_ops型別的[ioctl_ops], 分別實現檔案操作介面和V4L2介面。 */ struct video_device s3c_fimc_video_device[S3C_FIMC_MAX_CTRLS] = { [0] = { .vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES, .fops = &s3c_fimc_fops, .ioctl_ops = &s3c_fimc_v4l2_ops, .release = s3c_fimc_vdev_release, .name = "sc3_video0", }, [1] = { .vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES, .fops = &s3c_fimc_fops, .ioctl_ops = &s3c_fimc_v4l2_ops, .release = s3c_fimc_vdev_release, .name = "sc3_video1", }, [2] = { .vfl_type = VID_TYPE_OVERLAY | VID_TYPE_CAPTURE | VID_TYPE_CLIPPING | VID_TYPE_SCALES, .fops = &s3c_fimc_fops, .ioctl_ops = &s3c_fimc_v4l2_ops, .release = s3c_fimc_vdev_release, .name = "sc3_video2", }, }; struct video_device { /* device ops */ const struct v4l2_file_operations *fops; /* sysfs */ struct device dev; /* v4l device */ struct cdev *cdev; /* character device */ /* Set either parent or v4l2_dev if your driver uses v4l2_device */ struct device *parent; /* device parent */ struct v4l2_device *v4l2_dev; /* v4l2_device parent */ /* Control handler associated with this device node. May be NULL. */ struct v4l2_ctrl_handler *ctrl_handler; /* device info */ char name[32]; int vfl_type; /* 'minor' is set to -1 if the registration failed */ int minor; u16 num; /* added for TV */ int type2; int users; /* use bitops to set/clear/test flags */ unsigned long flags; /* attribute to differentiate multiple indices on one physical device */ int index; /* V4L2 file handles */ spinlock_t fh_lock; /* Lock for all v4l2_fhs */ struct list_head fh_list; /* List of struct v4l2_fh */ int debug; /* Activates debug level*/ /* Video standard vars */ v4l2_std_id tvnorms; /* Supported tv norms */ v4l2_std_id current_norm; /* Current tvnorm */ /* callbacks */ void (*release)(struct video_device *vdev); /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; }; /* s3c_fimc_core.c FIMC驅動遵循V4L2介面標準,其file_operations介面定義如下: 當應用程式通過系統呼叫open()開啟攝像頭裝置時,核心會最終找到s3c_fimc_open() 函式來開啟攝像頭。 應用程式通過read()獲取影象資料,該函式通過copy_to_user() 將核心空間所申請緩衝區中影象資料拷貝到使用者空間中開闢的影象資料區。 該函式沒有充分利用FIMC介面提供的ping-pong緩衝區,按字進行拷貝(memcpy), 十分耗時。 mmap函式將核心空間中申請到的影象緩衝區對映到應用程式所在的使用者空間, 這樣,應用程式申請到的buffer將指向核心空間的影象緩衝區,應用程式可以 (不拷貝)直接對影象進行操作。該函式配合V4L2標準中的環形緩衝區佇列, 節省了應用程式讀取影象資料所消耗的時間。 */ static const struct v4l2_file_operations s3c_fimc_fops = { //static const struct file_operations s3c_fimc_fops = { .owner = THIS_MODULE, .open = s3c_fimc_open, .release = s3c_fimc_release, .unlocked_ioctl = video_ioctl2, .read = s3c_fimc_read, .write = s3c_fimc_write, .mmap = s3c_fimc_mmap, .poll = s3c_fimc_poll, }; /* s3c_fimc_v4l2.c */ /************************* 應用程式通過ioctl介面使用這些函式,比如ioctl(fd,VIDIOC_S_FMT,&fmt) 用來設定影象格式,此時V4L2會將VIDIOC_S_FMT命令對映為s3c_fimc_v4l2_s_fmt_vid_cap()函式, 並將fmt指定的格式告知FIMC介面,FIMC會將OV9650傳遞過來的原始影象資料經過型別轉換傳遞迴應用程式。 s3c_fimc_v4l2_reqbufs()用於申請影象緩衝區, 該函式為應用程式在核心空間開闢ping-pong緩衝區。s3c_fimc_v4l2_qbuf() 函式將緩衝區組成環形緩衝佇列,當應用程式需要呼叫影象資料時,使用s3c_fimc_v4l2_dqbuf() 使指定的緩衝區出隊,緩衝區在出隊期間,不會被新來的影象資料覆蓋, 新到的影象資料會被傳送到環形佇列中指定緩衝區的下一個緩衝區。 由於FIMC控制器為P通道和C通道分別開闢了4個緩衝區,在核心初始化時已經申請到, 因此FIMC驅動中並不需要再重新申請。 *************************/ const struct v4l2_ioctl_ops s3c_fimc_v4l2_ops = { .vidioc_querycap = s3c_fimc_v4l2_querycap, .vidioc_g_fbuf = s3c_fimc_v4l2_g_fbuf, .vidioc_s_fbuf = s3c_fimc_v4l2_s_fbuf, .vidioc_enum_fmt_vid_cap = s3c_fimc_v4l2_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = s3c_fimc_v4l2_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = s3c_fimc_v4l2_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = s3c_fimc_v4l2_try_fmt_vid_cap, .vidioc_try_fmt_vid_overlay = s3c_fimc_v4l2_try_fmt_overlay, .vidioc_overlay = s3c_fimc_v4l2_overlay, .vidioc_g_ctrl = s3c_fimc_v4l2_g_ctrl, .vidioc_s_ctrl = s3c_fimc_v4l2_s_ctrl, .vidioc_streamon = s3c_fimc_v4l2_streamon, .vidioc_streamoff = s3c_fimc_v4l2_streamoff, .vidioc_g_input = s3c_fimc_v4l2_g_input, .vidioc_s_input = s3c_fimc_v4l2_s_input, .vidioc_g_output = s3c_fimc_v4l2_g_output, .vidioc_s_output = s3c_fimc_v4l2_s_output, .vidioc_enum_input = s3c_fimc_v4l2_enum_input, .vidioc_enum_output = s3c_fimc_v4l2_enum_output, .vidioc_reqbufs = s3c_fimc_v4l2_reqbufs, .vidioc_querybuf = s3c_fimc_v4l2_querybuf, .vidioc_qbuf = s3c_fimc_v4l2_qbuf, .vidioc_dqbuf = s3c_fimc_v4l2_dqbuf, .vidioc_cropcap = s3c_fimc_v4l2_cropcap, .vidioc_g_crop = s3c_fimc_v4l2_g_crop, .vidioc_s_crop = s3c_fimc_v4l2_s_crop, .vidioc_s_parm = s3c_fimc_v4l2_s_parm, };