Android S5PV210 fimc驱动分析 - fimc_dev.c

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


 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 shared\n", __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 fail\n", __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操作


 776 int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)
 777 {
 778     if (src >= tar * 64) {
 779         return -EINVAL;
 780     } else if (src >= tar * 32) {
 781         *ratio = 32;
 782         *shift = 5;
 783     } else if (src >= tar * 16) {
 784         *ratio = 16;
 785         *shift = 4;
 786     } else if (src >= tar * 8) {
 787         *ratio = 8;
 788         *shift = 3;
 789     } else if (src >= tar * 4) {
 790         *ratio = 4;
 791         *shift = 2;
 792     } else if (src >= tar * 2) {
 793         *ratio = 2;
 794         *shift = 1;
 795     } else {
 796         *ratio = 1;
 797         *shift = 0;
 798     }
 799 
 800     return 0;
 801 }

就算scaler 因子,src是原始分辨率,tar是目标分辨率,

@ratio返回缩放的比例,最大scaler不能超过64


 803 void fimc_get_nv12t_size(int img_hres, int img_vres,
 804                 int *y_size, int *cb_size, int rotate)
... 
fimc_get_nv12t_size,根据原始图像的水平分辨率,垂直分辨率计算 Y分量的size和CB分量的size,如果图像还要rotate,那么需要考虑rotate影响了对齐


 893 static int fimc_open(struct file *filp)
 894 {
...
 932     if (in_use == 1) {
 933         fimc_clk_en(ctrl, true);
 934 
 935         if (pdata->hw_ver == 0x40)
 936             fimc_hw_reset_camera(ctrl);
 937 
 938         /* Apply things to interface register */
 939         fimc_hwset_reset(ctrl);
 940 
 941         if (num_registered_fb > 0) {
 942             struct fb_info *fbinfo = registered_fb[0];
 943             ctrl->fb.lcd_hres = (int)fbinfo->var.xres;
 944             ctrl->fb.lcd_vres = (int)fbinfo->var.yres;
 945             fimc_info1("%s: fd.lcd_hres=%d fd.lcd_vres=%d\n",
 946                     __func__, ctrl->fb.lcd_hres,
 947                     ctrl->fb.lcd_vres);
 948         }
 949 
 950         ctrl->mem.curr = ctrl->mem.base;
 951         ctrl->status = FIMC_STREAMOFF;
 952 
 953         if (0 != ctrl->id)
 954             fimc_clk_en(ctrl, false);
 955     }
...

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 (24576 * SZ_1K)
#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (9900 * 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
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值