fimc-dev.c 是Samsung FIMC 设备的V4L2 驱动。上层应用直接操作这个设备,进行capture,图片处理,以及overlay输出
FIMC使用预分配的物理内存来申请DMA buffer,参数中的align指明申请buffer的对齐方式,对于FIMC capture来说,似乎output DMA要求4k对齐(尽管我没有在datasheet中找到),如果给定的DMA地址没有4K对齐,FIMC DMA控制器会很聪明的从4K对齐的地址开始传送数据,这会导致了帧数据偏移。
@i 参数指定了plane数,FIMC 输出支持很多种格式,有单层的比如YUYV,两层的V4L2_PIX_FMT_NV12,还有三层的V4L2_PIX_FMT_NV12T
单层格式输出申请一个buffer,两层格式输出申请两个buffer,三层则需申请三个buffer。
661 vma->vm_pgoff 表示vm_file内以PAGE_SIZE为单位的偏移,但是在这里应用层和内核使用另外一种约定的含义,buffer ID, 应用层调用mmap接口对fimc capture设备的buffer进行映射
static int fimc_open(struct file *filp) { struct fimc_control *ctrl; struct s3c_platform_fimc *pdata; struct fimc_prv_data *prv_data; int in_use; int ret; ctrl = video_get_drvdata(video_devdata(filp)); pdata = to_fimc_plat(ctrl->dev); mutex_lock(&ctrl->lock); in_use = atomic_read(&ctrl->in_use); if (in_use >= FIMC_MAX_CTXS || (in_use && 1 != ctrl->id)) { fimc_err("%s: Device busy.\n", __func__); ret = -EBUSY; goto resource_busy; } else { atomic_inc(&ctrl->in_use); } in_use = atomic_read(&ctrl->in_use); prv_data = kzalloc(sizeof(struct fimc_prv_data), GFP_KERNEL); if (!prv_data) { fimc_err("%s: not enough memory\n", __func__); ret = -ENOMEM; goto kzalloc_err; } prv_data->ctx_id = fimc_get_free_ctx(ctrl); if (prv_data->ctx_id < 0) { fimc_err("%s: Context busy flag not reset.\n", __func__); ret = -EBUSY; goto ctx_err; } prv_data->ctrl = ctrl; filp->private_data = prv_data; if (in_use == 1) { fimc_clk_en(ctrl, true); if (pdata->hw_ver == 0x40) fimc_hw_reset_camera(ctrl); /* Apply things to interface register */ fimc_hwset_reset(ctrl); if (num_registered_fb > 0) { struct fb_info *fbinfo = registered_fb[0]; ctrl->fb.lcd_hres = (int)fbinfo->var.xres; ctrl->fb.lcd_vres = (int)fbinfo->var.yres; fimc_info1("%s: fd.lcd_hres=%d fd.lcd_vres=%d\n", __func__, ctrl->fb.lcd_hres, ctrl->fb.lcd_vres); } ctrl->mem.curr = ctrl->mem.base; ctrl->status = FIMC_STREAMOFF; if (0 != ctrl->id) fimc_clk_en(ctrl, false); } mutex_unlock(&ctrl->lock); fimc_info1("%s opened.\n", ctrl->name); return 0; ctx_err: kfree(prv_data); kzalloc_err: atomic_dec(&ctrl->in_use); resource_busy: mutex_unlock(&ctrl->lock); return ret; }932 如果是第一次打开,那么需要使能mclk
939 调用fimc_hwset_reset 进行fimc控制器的reset过程951 FIMC会为每一个控制器预分配一段物理内存, mem.base 指向物理内存的起始地址,FIMC在执行DMA之前,需要为DMA分配物理内存,FIMC直接从这个预保留的空间进行分配
mem.curr记录着当前可用空间的起始位置在arch/arm/mach-s5pv210/mach-smdkc110.c中
//#define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (6144 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (24576 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (9900 * SZ_1K) //#define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (6144 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (24576 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0 (36864 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1 (36864 * SZ_1K) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD (S5PV210_LCD_WIDTH * \ S5PV210_LCD_HEIGHT * 4 * \ CONFIG_FB_S3C_NR_BUFFERS) #define S5PV210_VIDEO_SAMSUNG_MEMSIZE_JPEG (8192 * SZ_1K) 定义了media device需要的reserved 物理内存大小,物理内存的实际使用可能达不到定义的大小, 物理内存的使用大小 和图片大小以及queue buffer数量成正比的,也就是说图片分辨率Width x Height越高,queue buffer数量越多,实际使用的物理内存越大 所以这个物理内存大小要根据项目情况具体调整,当然你也可以不调,就是浪费点内存。1133 struct video_device fimc_video_device[FIMC_DEVICES] = { 1134 [0] = { 1135 .fops = &fimc_fops, 1136 .ioctl_ops = &fimc_v4l2_ops, 1137 .release = fimc_vdev_release, 1138 }, 1139 [1] = { 1140 .fops = &fimc_fops, 1141 .ioctl_ops = &fimc_v4l2_ops, 1142 .release = fimc_vdev_release, 1143 }, 1144 [2] = { 1145 .fops = &fimc_fops, 1146 .ioctl_ops = &fimc_v4l2_ops, 1147 .release = fimc_vdev_release, 1148 }, 1149 }; FIMC_DEVICES 一共有三个fimc0, fimc1, fimc2设备, fops定义了设备节点的文件操作函数; ioctl_ops定义了fimc向V4L2提供的所有ioctl操作集合1310 static int __devinit fimc_probe(struct platform_device *pdev) 1311 { 1312 struct s3c_platform_fimc *pdata; 1313 struct fimc_control *ctrl; 1314 struct clk *srclk; 1315 int ret; 1316 1317 if (!fimc_dev) { 1318 fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL); 1319 if (!fimc_dev) { 1320 dev_err(&pdev->dev, "%s: not enough memory\n", 1321 __func__); 1322 return -ENOMEM; 1323 } 1324 } 1325 1326 ctrl = fimc_register_controller(pdev); 1327 if (!ctrl) { 1328 printk(KERN_ERR "%s: cannot register fimc\n", __func__); 1329 goto err_alloc; 1330 } 1331 1332 pdata = to_fimc_plat(&pdev->dev); 1333 if (pdata->cfg_gpio) 1334 pdata->cfg_gpio(pdev); 1335 1336 /* Get fimc power domain regulator */ 1337 ctrl->regulator = regulator_get(&pdev->dev, "pd"); 1338 if (IS_ERR(ctrl->regulator)) { 1339 fimc_err("%s: failed to get resource %s\n", 1340 __func__, "s3c-fimc"); 1341 return PTR_ERR(ctrl->regulator); 1342 } 1343 1344 /* fimc source clock */ 1345 srclk = clk_get(&pdev->dev, pdata->srclk_name); 1346 if (IS_ERR(srclk)) { 1347 fimc_err("%s: failed to get source clock of fimc\n", 1348 __func__); 1349 goto err_v4l2; 1350 } 1351 1352 /* fimc clock */ 1353 ctrl->clk = clk_get(&pdev->dev, pdata->clk_name); 1354 if (IS_ERR(ctrl->clk)) { 1355 fimc_err("%s: failed to get fimc clock source\n", 1356 __func__); 1357 goto err_v4l2; 1358 } 1359 1360 /* set parent for mclk */ 1361 clk_set_parent(ctrl->clk, srclk); 1362 1363 /* set rate for mclk */ 1364 clk_set_rate(ctrl->clk, pdata->clk_rate); 1365 1366 /* V4L2 device-subdev registration */ 1367 ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev); 1368 if (ret) { 1369 fimc_err("%s: v4l2 device register failed\n", __func__); 1370 goto err_fimc; 1371 } 1372 1373 /* things to initialize once */ 1374 if (!fimc_dev->initialized) { 1375 ret = fimc_init_global(pdev); 1376 if (ret) 1377 goto err_v4l2; 1378 } 1379 1380 /* video device register */ 1381 ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id); 1382 if (ret) { 1383 fimc_err("%s: cannot register video driver\n", __func__); 1384 goto err_v4l2; 1385 } 1386 1387 video_set_drvdata(ctrl->vd, ctrl); 1388 1389 ret = device_create_file(&(pdev->dev), &dev_attr_log_level); 1390 if (ret < 0) { 1391 fimc_err("failed to add sysfs entries\n"); 1392 goto err_global; 1393 } 1394 printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id); 1395 1396 return 0; 1397 1398 err_global: 1399 video_unregister_device(ctrl->vd); 1400 1401 err_v4l2: 1402 v4l2_device_unregister(&ctrl->v4l2_dev); 1403 1404 err_fimc: 1405 fimc_unregister_controller(pdev); 1406 1407 err_alloc: 1408 kfree(fimc_dev); 1409 return -EINVAL; 1410 1411 } 1333 ~ 1334 调用平台的gpio设置函数,一般来说,这个用来设置external CameraA/CameraB的输入输出 1344 ~ 1364 设置mclk,mclk的频率由sensor的输出图像尺寸, 如果外围sensor自身有晶振,那么CPU不需要对外提供mclk 1381 ~ 1385 注册一个video device,会生成设备节点/dev/videoX