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>  
原文地址:https://www.cnblogs.com/cainiaoaixuexi/p/3333306.html