s5pv210 fimc 之 fimc-dev.c
2024-12-16 来源:cnblogs
fimc-dev.c 是Samsung FIMC 设备的V4L2 驱动。上层应用直接操作这个设备,进行capture,图片处理,以及overlay输出
http://blog.csdn.net/cxw3506/article/details/8476263
43 int fimc_dma_alloc(struct fimc_control *ctrl, struct fimc_buf_set *bs,
44 int i, int align)
45 {
46 dma_addr_t end, *curr;
47
48 mutex_lock(&ctrl->alloc_lock);
49
50 end = ctrl->mem.base + ctrl->mem.size;
51 curr = &ctrl->mem.curr;
52
53 if (!bs->length[i])
54 return -EINVAL;
55
56 if (!align) {
57 if (*curr + bs->length[i] > end) {
58 goto overflow;
59 } else {
60 bs->base[i] = *curr;
61 bs->garbage[i] = 0;
62 *curr += bs->length[i];
63 }
64 } else {
65 if (ALIGN(*curr, align) + bs->length[i] > end)
66 goto overflow;
67 else {
68 bs->base[i] = ALIGN(*curr, align);
69 bs->garbage[i] = ALIGN(*curr, align) - *curr;
70 *curr += (bs->length[i] + bs->garbage[i]);
71 }
72 }
73
74 mutex_unlock(&ctrl->alloc_lock);
75
76 return 0;
77
78 overflow:
79 bs->base[i] = 0;
80 bs->length[i] = 0;
81 bs->garbage[i] = 0;
82
83 mutex_unlock(&ctrl->alloc_lock);
84
85 return -ENOMEM;
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。
88 void fimc_dma_free(struct fimc_control *ctrl, struct fimc_buf_set *bs, int i)
89 {
90 int total = bs->length[i] + bs->garbage[i];
91 mutex_lock(&ctrl->alloc_lock);
92
93 if (bs->base[i]) {
94 if (ctrl->mem.curr - total >= ctrl->mem.base)
95 ctrl->mem.curr -= total;
96
97 bs->base[i] = 0;
98 bs->length[i] = 0;
99 bs->garbage[i] = 0;
100 }
101
102 mutex_unlock(&ctrl->alloc_lock);
103 }
这个函数有问题,93 ~ 95 成立的条件是bs->base[i]所占的地址必须在ctrl->mem.base最后面,这就要求释放顺序必须从bs的最后一个节点向前释放。
655 static inline int fimc_mmap_cap(struct file *filp, struct vm_area_struct *vma)
656 {
657 struct fimc_prv_data *prv_data =
658 (struct fimc_prv_data *)filp->private_data;
659 struct fimc_control *ctrl = prv_data->ctrl;
660 u32 size = vma->vm_end - vma->vm_start;
661 u32 pfn, idx = vma->vm_pgoff;
662
663 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
664 vma->vm_flags |= VM_RESERVED;
665
666 /*
667 * page frame number of the address for a source frame
668 * to be stored at.
669 */
670 pfn = __phys_to_pfn(ctrl->cap->bufs[idx].base[0]);
671
672 if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED)) {
673 fimc_err('%s: writable mapping must be sharedn', __func__);
674 return -EINVAL;
675 }
676
677 if (remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot)) {
678 fimc_err('%s: mmap failn', __func__);
679 return -EINVAL;
680 }
681
682 return 0;
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进行映射
700 static u32 fimc_poll(struct file *filp, poll_table *wait)
701 {
702 struct fimc_prv_data *prv_data =
703 (struct fimc_prv_data *)filp->private_data;
704 struct fimc_control *ctrl = prv_data->ctrl;
705 struct fimc_capinfo *cap = ctrl->cap;
706 u32 mask = 0;
707
708 if (cap) {
709 if (cap->irq || (ctrl->status != FIMC_STREAMON)) {
710 mask = POLLIN | POLLRDNORM;
711 cap->irq = 0;
712 } else {
713 poll_wait(filp, &ctrl->wq, wait);
714 }
715 }
716
717 return mask;
718 }
fimc_poll向上层应用提供了等待机制,应用程序poll fimc设备并阻塞,直到cap或者output中断处理函数唤醒
732 u32 fimc_mapping_rot_flip(u32 rot, u32 flip)
733 {
734 u32 ret = 0;
735
736 switch (rot) {
737 case 0:
738 if (flip & FIMC_XFLIP)
739 ret |= FIMC_XFLIP;
740
741 if (flip & FIMC_YFLIP)
742 ret |= FIMC_YFLIP;
743 break;
744
745 case 90:
746 ret = FIMC_ROT;
747 if (flip & FIMC_XFLIP)
748 ret |= FIMC_XFLIP;
749
750 if (flip & FIMC_YFLIP)
751 ret |= FIMC_YFLIP;
752 break;
753
754 case 180:
755 ret = (FIMC_XFLIP | FIMC_YFLIP);
756 if (flip & FIMC_XFLIP)
757 ret &= ~FIMC_XFLIP;
758
759 if (flip & FIMC_YFLIP)
760 ret &= ~FIMC_YFLIP;
761 break;
762
763 case 270:
764 ret = (FIMC_XFLIP | FIMC_YFLIP | FIMC_ROT);
765 if (flip & FIMC_XFLIP)
766 ret &= ~FIMC_XFLIP;
767
768 if (flip & FIMC_YFLIP)
769 ret &= ~FIMC_YFLIP;
770 break;
771 }
772
773 return ret;
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 memoryn', __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=%dn',
__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 memoryn',
1321 __func__);
1322 return -ENOMEM;
1323 }
1324 }
1325
1326 ctrl = fimc_register_controller(pdev);
1327 if (!ctrl) {
1328 printk(KERN_ERR '%s: cannot register fimcn', __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 %sn',
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 fimcn',
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 sourcen',
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 failedn', __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 drivern', __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 entriesn');
1392 goto err_global;
1393 }
1394 printk(KERN_INFO 'FIMC%d registered successfullyn', 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
- 六大全新产品系列推出,MCX A微控制器家族迎来创新
- 意法半导体全新STM32C5系列,重新定义入门级微控制器性能与价值,赋能万千智能设备
- 模组复用与整机重测在SRRC、CCC、CTA/NAL认证中的实践操作指南
- 有源晶振与无源晶振的六大区别详解
- 英飞凌持续巩固全球微控制器市场领导地位
- 使用 Keil Studio for Visual Studio Code开发 STM32 设备
- 蓝牙信道探测技术原理与开发套件实践
- Microchip 推出生产就绪型全栈边缘 AI 解决方案,赋能MCU和MPU实现 智能实时决策
- 从控制到系统:TI利用边缘AI重塑嵌入式MCU的边界
- LoRa、LoRaWAN、NB-IoT与4G DTU技术对比及工业无线方案选型分析




