s5pv210 fimc 之 fimc-dev.c

fimc-dev.c 是Samsung FIMC 设备的V4L2 驱动。上层应用直接操作这个设备,进行capture,图片处理,以及overlay输出


  1. 43 int fimc_dma_alloc(struct fimc_control *ctrl, struct fimc_buf_set *bs,  
  2.  44                             int i, int align)  
  3.  45 {  
  4.  46     dma_addr_t end, *curr;  
  5.  47  
  6.  48     mutex_lock(&ctrl->alloc_lock);  
  7.  49  
  8.  50     end = ctrl->mem.base + ctrl->mem.size;  
  9.  51     curr = &ctrl->mem.curr;  
  10.  52  
  11.  53     if (!bs->length[i])  
  12.  54         return -EINVAL;  
  13.  55  
  14.  56     if (!align) {  
  15.  57         if (*curr + bs->length[i] > end) {  
  16.  58             goto overflow;  
  17.  59         } else {  
  18.  60             bs->base[i] = *curr;  
  19.  61             bs->garbage[i] = 0;  
  20.  62             *curr += bs->length[i];  
  21.  63         }  
  22.  64     } else {  
  23.  65         if (ALIGN(*curr, align) + bs->length[i] > end)  
  24.  66             goto overflow;  
  25.  67         else {  
  26.  68             bs->base[i] = ALIGN(*curr, align);  
  27.  69             bs->garbage[i] = ALIGN(*curr, align) - *curr;  
  28.  70             *curr += (bs->length[i] + bs->garbage[i]);  
  29.  71         }  
  30.  72     }  
  31.  73  
  32.  74     mutex_unlock(&ctrl->alloc_lock);  
  33.  75  
  34.  76     return 0;  
  35.  77  
  36.  78 overflow:  
  37.  79     bs->base[i] = 0;  
  38.  80     bs->length[i] = 0;  
  39.  81     bs->garbage[i] = 0;  
  40.  82  
  41.  83     mutex_unlock(&ctrl->alloc_lock);  
  42.  84  
  43.  85     return -ENOMEM;  
  44.  86 }  
这个函数很简单,之所以提出来说下,是因为我在DMA对齐问题上卡了一个多星期

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。


  1.  88 void fimc_dma_free(struct fimc_control *ctrl, struct fimc_buf_set *bs, int i)  
  2.  89 {  
  3.  90     int total = bs->length[i] + bs->garbage[i];  
  4.  91     mutex_lock(&ctrl->alloc_lock);  
  5.  92   
  6.  93     if (bs->base[i]) {  
  7.  94         if (ctrl->mem.curr - total >= ctrl->mem.base)  
  8.  95             ctrl->mem.curr -= total;  
  9.  96   
  10.  97         bs->base[i] = 0;  
  11.  98         bs->length[i] = 0;  
  12.  99         bs->garbage[i] = 0;  
  13. 100     }  
  14. 101   
  15. 102     mutex_unlock(&ctrl->alloc_lock);  
  16. 103 }  

这个函数有问题,93 ~ 95 成立的条件是bs->base[i]所占的地址必须在ctrl->mem.base最后面,这就要求释放顺序必须从bs的最后一个节点向前释放。


  1. 655 static inline int fimc_mmap_cap(struct file *filp, struct vm_area_struct *vma)  
  2. 656 {  
  3. 657     struct fimc_prv_data *prv_data =  
  4. 658                 (struct fimc_prv_data *)filp->private_data;  
  5. 659     struct fimc_control *ctrl = prv_data->ctrl;  
  6. 660     u32 size = vma->vm_end - vma->vm_start;  
  7. 661     u32 pfn, idx = vma->vm_pgoff;  
  8. 662   
  9. 663     vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);  
  10. 664     vma->vm_flags |= VM_RESERVED;  
  11. 665   
  12. 666     /* 
  13. 667      * page frame number of the address for a source frame 
  14. 668      * to be stored at. 
  15. 669      */  
  16. 670     pfn = __phys_to_pfn(ctrl->cap->bufs[idx].base[0]);  
  17. 671   
  18. 672     if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {  
  19. 673         fimc_err("%s: writable mapping must be shared\n", __func__);  
  20. 674         return -EINVAL;  
  21. 675     }  
  22. 676   
  23. 677     if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {  
  24. 678         fimc_err("%s: mmap fail\n", __func__);  
  25. 679         return -EINVAL;  
  26. 680     }  
  27. 681   
  28. 682     return 0;  
  29. 683 }  
fimc capture 设备的mmap实现,ctrl->cap->bufs[idx]是fimc capture设备申请的buffer,mmap就是把这个buffer映射到应用程序空间

661 vma->vm_pgoff 表示vm_file内以PAGE_SIZE为单位的偏移,但是在这里应用层和内核使用另外一种约定的含义,buffer ID, 应用层调用mmap接口对fimc capture设备的buffer进行映射


  1. 700 static u32 fimc_poll(struct file *filp, poll_table *wait)  
  2. 701 {  
  3. 702     struct fimc_prv_data *prv_data =  
  4. 703                 (struct fimc_prv_data *)filp->private_data;  
  5. 704     struct fimc_control *ctrl = prv_data->ctrl;  
  6. 705     struct fimc_capinfo *cap = ctrl->cap;  
  7. 706     u32 mask = 0;  
  8. 707   
  9. 708     if (cap) {  
  10. 709         if (cap->irq || (ctrl->status != FIMC_STREAMON)) {  
  11. 710             mask = POLLIN | POLLRDNORM;  
  12. 711             cap->irq = 0;  
  13. 712         } else {  
  14. 713             poll_wait(filp, &ctrl->wq, wait);  
  15. 714         }  
  16. 715     }  
  17. 716   
  18. 717     return mask;  
  19. 718 }  

fimc_poll向上层应用提供了等待机制,应用程序poll fimc设备并阻塞,直到cap或者output中断处理函数唤醒


  1. 732 u32 fimc_mapping_rot_flip(u32 rot, u32 flip)  
  2. 733 {  
  3. 734     u32 ret = 0;  
  4. 735   
  5. 736     switch (rot) {  
  6. 737     case 0:  
  7. 738         if (flip & FIMC_XFLIP)  
  8. 739             ret |= FIMC_XFLIP;  
  9. 740   
  10. 741         if (flip & FIMC_YFLIP)  
  11. 742             ret |= FIMC_YFLIP;  
  12. 743         break;  
  13. 744   
  14. 745     case 90:  
  15. 746         ret = FIMC_ROT;  
  16. 747         if (flip & FIMC_XFLIP)  
  17. 748             ret |= FIMC_XFLIP;  
  18. 749   
  19. 750         if (flip & FIMC_YFLIP)  
  20. 751             ret |= FIMC_YFLIP;  
  21. 752         break;  
  22. 753   
  23. 754     case 180:  
  24. 755         ret = (FIMC_XFLIP | FIMC_YFLIP);  
  25. 756         if (flip & FIMC_XFLIP)  
  26. 757             ret &= ~FIMC_XFLIP;  
  27. 758   
  28. 759         if (flip & FIMC_YFLIP)  
  29. 760             ret &= ~FIMC_YFLIP;  
  30. 761         break;  
  31. 762   
  32. 763     case 270:  
  33. 764         ret = (FIMC_XFLIP | FIMC_YFLIP | FIMC_ROT);  
  34. 765         if (flip & FIMC_XFLIP)  
  35. 766             ret &= ~FIMC_XFLIP;  
  36. 767   
  37. 768         if (flip & FIMC_YFLIP)  
  38. 769             ret &= ~FIMC_YFLIP;  
  39. 770         break;  
  40. 771     }  
  41. 772   
  42. 773     return ret;  
  43. 774 }  

rot会影响flip的结果,该函数映射(合并)rot和 flip操作

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
  




  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值