zoukankan      html  css  js  c++  java
  • s5pv210 fimc 之 fimc-dev.c

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

     http://blog.csdn.net/cxw3506/article/details/8476263

    [cpp] view plaincopy
     
    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。

    [cpp] view plaincopy
     
    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的最后一个节点向前释放。

    [cpp] view plaincopy
     
    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 ", __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 ", __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进行映射

    [cpp] view plaincopy
     
    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中断处理函数唤醒

    [cpp] view plaincopy
     
    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操作

    [cpp] view plaincopy
     
    1. static int fimc_open(struct file *filp)  
    2. {  
    3.     struct fimc_control *ctrl;  
    4.     struct s3c_platform_fimc *pdata;  
    5.     struct fimc_prv_data *prv_data;  
    6.     int in_use;  
    7.     int ret;  
    8.   
    9.     ctrl = video_get_drvdata(video_devdata(filp));  
    10.     pdata = to_fimc_plat(ctrl->dev);  
    11.   
    12.     mutex_lock(&ctrl->lock);  
    13.   
    14.     in_use = atomic_read(&ctrl->in_use);  
    15.     if (in_use >= FIMC_MAX_CTXS || (in_use && 1 != ctrl->id)) {  
    16.         fimc_err("%s: Device busy. ", __func__);  
    17.         ret = -EBUSY;  
    18.         goto resource_busy;  
    19.     } else {  
    20.         atomic_inc(&ctrl->in_use);  
    21.     }  
    22.     in_use = atomic_read(&ctrl->in_use);  
    23.   
    24.     prv_data = kzalloc(sizeof(struct fimc_prv_data), GFP_KERNEL);  
    25.     if (!prv_data) {  
    26.         fimc_err("%s: not enough memory ", __func__);  
    27.         ret = -ENOMEM;  
    28.         goto kzalloc_err;  
    29.     }  
    30.   
    31.     prv_data->ctx_id = fimc_get_free_ctx(ctrl);  
    32.     if (prv_data->ctx_id < 0) {  
    33.         fimc_err("%s: Context busy flag not reset. ", __func__);  
    34.         ret = -EBUSY;  
    35.         goto ctx_err;  
    36.     }  
    37.     prv_data->ctrl = ctrl;  
    38.     filp->private_data = prv_data;  
    39.   
    40.     if (in_use == 1) {  
    41.         fimc_clk_en(ctrl, true);  
    42.   
    43.         if (pdata->hw_ver == 0x40)  
    44.             fimc_hw_reset_camera(ctrl);  
    45.   
    46.         /* Apply things to interface register */  
    47.         fimc_hwset_reset(ctrl);  
    48.   
    49.         if (num_registered_fb > 0) {  
    50.             struct fb_info *fbinfo = registered_fb[0];  
    51.             ctrl->fb.lcd_hres = (int)fbinfo->var.xres;  
    52.             ctrl->fb.lcd_vres = (int)fbinfo->var.yres;  
    53.             fimc_info1("%s: fd.lcd_hres=%d fd.lcd_vres=%d ",  
    54.                     __func__, ctrl->fb.lcd_hres,  
    55.                     ctrl->fb.lcd_vres);  
    56.         }  
    57.   
    58.         ctrl->mem.curr = ctrl->mem.base;  
    59.         ctrl->status = FIMC_STREAMOFF;  
    60.   
    61.         if (0 != ctrl->id)  
    62.             fimc_clk_en(ctrl, false);  
    63.     }  
    64.   
    65.     mutex_unlock(&ctrl->lock);  
    66.   
    67.     fimc_info1("%s opened. ", ctrl->name);  
    68.   
    69.     return 0;  
    70.   
    71. ctx_err:  
    72.     kfree(prv_data);  
    73.   
    74. kzalloc_err:  
    75.     atomic_dec(&ctrl->in_use);  
    76.   
    77. resource_busy:  
    78.     mutex_unlock(&ctrl->lock);  
    79.     return ret;  
    80. }  

    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中

    [cpp] view plaincopy
     
      1. //#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (6144 * SZ_1K)  
      2. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (24576 * SZ_1K)  
      3. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (9900 * SZ_1K)  
      4. //#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (6144 * SZ_1K)  
      5. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (24576 * SZ_1K)  
      6. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0 (36864 * SZ_1K)  
      7. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1 (36864 * SZ_1K)  
      8. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD (S5PV210_LCD_WIDTH *   
      9.                          S5PV210_LCD_HEIGHT * 4 *   
      10.                          CONFIG_FB_S3C_NR_BUFFERS)  
      11. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_JPEG (8192 * SZ_1K)<span>  
      12. <span style="color:#FF0000;">定义了media device需要的reserved 物理内存大小,物理内存的实际使用可能达不到定义的大小, </span></span><span style="color:#FF0000;"><span><span class="tag"></span><span class="tag-name"></span><span class="tag"></span><span class="tag"></span><span class="tag-name"></span><span class="tag"></span><span></span></span><span><span class="tag"></span><span>  
      13. 物理内存的使用大小 和图片大小以及queue buffer数量成正比的,也就是说图片分辨率Width x Height越高,queue buffer数量越多,实际使用的物理内存越大</span><span class="tag"></span><span class="tag-name"></span><span class="tag"></span><span></span></span><span><span>  
      14. 所以这个物理内存大小要根据项目情况具体调整,当然你也可以不调,就是浪费点内存。  
      15. </span></span></span>  
      16. <pre name="code" class="cpp">1133 struct video_device fimc_video_device[FIMC_DEVICES] = {    
      17. 1134     [0] = {    
      18. 1135         .fops = &fimc_fops,    
      19. 1136         .ioctl_ops = &fimc_v4l2_ops,    
      20. 1137         .release = fimc_vdev_release,    
      21. 1138     },    
      22. 1139     [1] = {    
      23. 1140         .fops = &fimc_fops,    
      24. 1141         .ioctl_ops = &fimc_v4l2_ops,    
      25. 1142         .release = fimc_vdev_release,    
      26. 1143     },    
      27. 1144     [2] = {    
      28. 1145         .fops = &fimc_fops,    
      29. 1146         .ioctl_ops = &fimc_v4l2_ops,    
      30. 1147         .release = fimc_vdev_release,    
      31. 1148     },    
      32. 1149 };  
      33. FIMC_DEVICES 一共有三个fimc0, fimc1, fimc2设备,    
      34.     
      35. fops定义了设备节点的文件操作函数; ioctl_ops定义了fimc向V4L2提供的所有ioctl操作集合</pre><pre name="code" class="cpp">1310 static int __devinit fimc_probe(struct platform_device *pdev)    
      36. 1311 {    
      37. 1312     struct s3c_platform_fimc *pdata;    
      38. 1313     struct fimc_control *ctrl;    
      39. 1314     struct clk *srclk;    
      40. 1315     int ret;    
      41. 1316    
      42. 1317     if (!fimc_dev) {    
      43. 1318         fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);    
      44. 1319         if (!fimc_dev) {    
      45. 1320             dev_err(&pdev->dev, "%s: not enough memory ",    
      46. 1321                 __func__);    
      47. 1322             return -ENOMEM;    
      48. 1323         }    
      49. 1324     }    
      50. 1325    
      51. 1326     ctrl = fimc_register_controller(pdev);    
      52. 1327     if (!ctrl) {    
      53. 1328         printk(KERN_ERR "%s: cannot register fimc ", __func__);    
      54. 1329         goto err_alloc;    
      55. 1330     }    
      56. 1331    
      57. 1332     pdata = to_fimc_plat(&pdev->dev);    
      58. 1333     if (pdata->cfg_gpio)    
      59. 1334         pdata->cfg_gpio(pdev);    
      60. 1335    
      61. 1336     /* Get fimc power domain regulator */    
      62. 1337     ctrl->regulator = regulator_get(&pdev->dev, "pd");    
      63. 1338     if (IS_ERR(ctrl->regulator)) {    
      64. 1339         fimc_err("%s: failed to get resource %s ",    
      65. 1340                 __func__, "s3c-fimc");    
      66. 1341         return PTR_ERR(ctrl->regulator);    
      67. 1342     }    
      68. 1343    
      69. 1344     /* fimc source clock */    
      70. 1345     srclk = clk_get(&pdev->dev, pdata->srclk_name);    
      71. 1346     if (IS_ERR(srclk)) {    
      72. 1347         fimc_err("%s: failed to get source clock of fimc ",    
      73. 1348                 __func__);    
      74. 1349         goto err_v4l2;    
      75. 1350     }    
      76. 1351     
      77. 1352     /* fimc clock */    
      78. 1353     ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);    
      79. 1354     if (IS_ERR(ctrl->clk)) {    
      80. 1355         fimc_err("%s: failed to get fimc clock source ",    
      81. 1356             __func__);    
      82. 1357         goto err_v4l2;    
      83. 1358     }    
      84. 1359     
      85. 1360     /* set parent for mclk */    
      86. 1361     clk_set_parent(ctrl->clk, srclk);    
      87. 1362     
      88. 1363     /* set rate for mclk */    
      89. 1364     clk_set_rate(ctrl->clk, pdata->clk_rate);    
      90. 1365     
      91. 1366     /* V4L2 device-subdev registration */    
      92. 1367     ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);    
      93. 1368     if (ret) {    
      94. 1369         fimc_err("%s: v4l2 device register failed ", __func__);    
      95. 1370         goto err_fimc;    
      96. 1371     }    
      97. 1372     
      98. 1373     /* things to initialize once */    
      99. 1374     if (!fimc_dev->initialized) {    
      100. 1375         ret = fimc_init_global(pdev);    
      101. 1376         if (ret)    
      102. 1377             goto err_v4l2;    
      103. 1378     }    
      104. 1379     
      105. 1380     /* video device register */    
      106. 1381     ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);    
      107. 1382     if (ret) {    
      108. 1383         fimc_err("%s: cannot register video driver ", __func__);    
      109. 1384         goto err_v4l2;    
      110. 1385     }    
      111. 1386     
      112. 1387     video_set_drvdata(ctrl->vd, ctrl);    
      113. 1388     
      114. 1389     ret = device_create_file(&(pdev->dev), &dev_attr_log_level);    
      115. 1390     if (ret < 0) {    
      116. 1391         fimc_err("failed to add sysfs entries ");    
      117. 1392         goto err_global;    
      118. 1393     }    
      119. 1394     printk(KERN_INFO "FIMC%d registered successfully ", ctrl->id);    
      120. 1395     
      121. 1396     return 0;    
      122. 1397     
      123. 1398 err_global:    
      124. 1399     video_unregister_device(ctrl->vd);    
      125. 1400     
      126. 1401 err_v4l2:    
      127. 1402     v4l2_device_unregister(&ctrl->v4l2_dev);    
      128. 1403     
      129. 1404 err_fimc:    
      130. 1405     fimc_unregister_controller(pdev);    
      131. 1406     
      132. 1407 err_alloc:    
      133. 1408     kfree(fimc_dev);    
      134. 1409     return -EINVAL;    
      135. 1410     
      136. 1411 }    
      137.     
      138. 1333 ~ 1334 调用平台的gpio设置函数,一般来说,这个用来设置external CameraA/CameraB的输入输出  
      139.     
      140. 1344 ~ 1364 设置mclk,mclk的频率由sensor的输出图像尺寸, 如果外围sensor自身有晶振,那么CPU不需要对外提供mclk  
      141.     
      142. 1381 ~ 1385 注册一个video device,会生成设备节点/dev/videoX  
      143.   </pre><br>  
      144. <br>  
      145. <p></p>  
      146. <pre></pre>  
      147. <p></p>  
      148. <p><br>  
      149. </p>  
  • 相关阅读:
    java配置文件读取
    简易表单验证
    Enter键提交表单
    background-attachment:fixed应用
    随机生成密钥
    javascript/jquery判断是否为undefined或是null!
    jQuery获取和设置disabled属性、背景图片路径
    $(function(){})里面不能声明定义函数
    CSS浏览器兼容性----Hack
    数组快速排序
  • 原文地址:https://www.cnblogs.com/cainiaoaixuexi/p/3333306.html
Copyright © 2011-2022 走看看