Android S5PV210 fimc驱动分析 - fimc_dev.c


分类: Samsung S5PV210   405人阅读  评论(0)  收藏  举报

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


[cpp]  view plain copy
  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 plain copy
  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 plain copy
  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进行映射


[cpp]  view plain copy
  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 plain copy
  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 plain copy
  1. 776 int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift)  
  2. 777 {  
  3. 778     if (src >= tar * 64) {  
  4. 779         return -EINVAL;  
  5. 780     } else if (src >= tar * 32) {  
  6. 781         *ratio = 32;  
  7. 782         *shift = 5;  
  8. 783     } else if (src >= tar * 16) {  
  9. 784         *ratio = 16;  
  10. 785         *shift = 4;  
  11. 786     } else if (src >= tar * 8) {  
  12. 787         *ratio = 8;  
  13. 788         *shift = 3;  
  14. 789     } else if (src >= tar * 4) {  
  15. 790         *ratio = 4;  
  16. 791         *shift = 2;  
  17. 792     } else if (src >= tar * 2) {  
  18. 793         *ratio = 2;  
  19. 794         *shift = 1;  
  20. 795     } else {  
  21. 796         *ratio = 1;  
  22. 797         *shift = 0;  
  23. 798     }  
  24. 799   
  25. 800     return 0;  
  26. 801 }  

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

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


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


[html]  view plain copy
  1.  893 static int fimc_open(struct file *filp)  
  2.  894 {  
  3. ...  
  4. <pre name="code" class="cpp"> 932     if (in_use == 1) {  
  5.  933         fimc_clk_en(ctrl, true);  
  6.  934   
  7.  935         if (pdata->hw_ver == 0x40)  
  8.  936             fimc_hw_reset_camera(ctrl);  
  9.  937   
  10.  938         /* Apply things to interface register */  
  11.  939         fimc_hwset_reset(ctrl);  
  12.  940   
  13.  941         if (num_registered_fb > 0) {  
  14.  942             struct fb_info *fbinfo = registered_fb[0];  
  15.  943             ctrl->fb.lcd_hres = (int)fbinfo->var.xres;  
  16.  944             ctrl->fb.lcd_vres = (int)fbinfo->var.yres;  
  17.  945             fimc_info1("%s: fd.lcd_hres=%d fd.lcd_vres=%d\n",  
  18.  946                     __func__, ctrl->fb.lcd_hres,  
  19.  947                     ctrl->fb.lcd_vres);  
  20.  948         }  
  21.  949   
  22.  950         ctrl->mem.curr = ctrl->mem.base;  
  23.  951         ctrl->status = FIMC_STREAMOFF;  
  24.  952   
  25.  953         if (0 != ctrl->id)  
  26.  954             fimc_clk_en(ctrl, false);  
  27.  955     }</pre>...<br>  
  28. <br>  
  29. 932 如果是第一次打开,那么需要使能mclk  
  30. <pre></pre>  
  31. <p></p>  
  32. <pre></pre>  
  33. <p></p>  
  34. <p>939 调用fimc_hwset_reset 进行fimc控制器的reset过程</p>  
  35. <p>951 FIMC会为每一个控制器预分配一段物理内存, mem.base 指向物理内存的起始地址,FIMC在执行DMA之前,需要为DMA分配物理内存,FIMC直接从这个预保留的空间进行分配</p>  
  36. <p>mem.curr记录着当前可用空间的起始位置。</p>  
  37. <p>在arch/arm/mach-s5pv210/mach-smdkc110.c中:</p>  
  38. <p></p>  
  39. <pre name="code" class="cpp">#define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC0 (24576 * SZ_1K)  
  40. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC1 (9900 * SZ_1K)  
  41. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMC2 (24576 * SZ_1K)  
  42. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC0 (36864 * SZ_1K)  
  43. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_MFC1 (36864 * SZ_1K)  
  44. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_FIMD (S5PV210_LCD_WIDTH * \  
  45.                          S5PV210_LCD_HEIGHT * 4 * \  
  46.                          CONFIG_FB_S3C_NR_BUFFERS)  
  47. #define  S5PV210_VIDEO_SAMSUNG_MEMSIZE_JPEG (8192 * SZ_1K)</pre><br>  
  48. 定义了media device需要的reserved 物理内存大小,物理内存的实际使用可能达不到定义的大小,  
  49. <p></p>  
  50. <p>物理内存的使用大小 和图片大小以及queue buffer数量成正比的,也就是说图片分辨率Width x Height越高,queue buffer数量越多,实际使用的物理内存越大</p>  
  51. <p>所以这个物理内存大小要根据项目情况具体调整,当然你也可以不调,就是浪费点内存。<br>  
  52. </p>  
  53. <p><br>  
  54. </p>  
  55. <p></p>  
  56. <pre name="code" class="html">1133 struct video_device fimc_video_device[FIMC_DEVICES] = {  
  57. 1134     [0] = {  
  58. 1135         .fops = &fimc_fops,  
  59. 1136         .ioctl_ops = &fimc_v4l2_ops,  
  60. 1137         .release = fimc_vdev_release,  
  61. 1138     },  
  62. 1139     [1] = {  
  63. 1140         .fops = &fimc_fops,  
  64. 1141         .ioctl_ops = &fimc_v4l2_ops,  
  65. 1142         .release = fimc_vdev_release,  
  66. 1143     },  
  67. 1144     [2] = {  
  68. 1145         .fops = &fimc_fops,  
  69. 1146         .ioctl_ops = &fimc_v4l2_ops,  
  70. 1147         .release = fimc_vdev_release,  
  71. 1148     },  
  72. 1149 };</pre>FIMC_DEVICES 一共有三个fimc0, fimc1, fimc2设备,  
  73. <p></p>  
  74. <p>fops定义了设备节点的文件操作函数; ioctl_ops定义了fimc向V4L2提供的所有ioctl操作集合<br>  
  75. <br>  
  76. <br>  
  77. </p>  
  78. <pre name="code" class="html">1310 static int __devinit fimc_probe(struct platform_device *pdev)  
  79. 1311 {  
  80. 1312     struct s3c_platform_fimc *pdata;  
  81. 1313     struct fimc_control *ctrl;  
  82. 1314     struct clk *srclk;  
  83. 1315     int ret;  
  84. 1316  
  85. 1317     if (!fimc_dev) {  
  86. 1318         fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);  
  87. 1319         if (!fimc_dev) {  
  88. 1320             dev_err(&pdev->dev, "%s: not enough memory\n",  
  89. 1321                 __func__);  
  90. 1322             return -ENOMEM;  
  91. 1323         }  
  92. 1324     }  
  93. 1325  
  94. 1326     ctrl = fimc_register_controller(pdev);  
  95. 1327     if (!ctrl) {  
  96. 1328         printk(KERN_ERR "%s: cannot register fimc\n", __func__);  
  97. 1329         goto err_alloc;  
  98. 1330     }  
  99. 1331  
  100. 1332     pdata = to_fimc_plat(&pdev->dev);  
  101. 1333     if (pdata->cfg_gpio)  
  102. 1334         pdata->cfg_gpio(pdev);  
  103. 1335  
  104. 1336     /* Get fimc power domain regulator */  
  105. 1337     ctrl->regulator = regulator_get(&pdev->dev, "pd");  
  106. 1338     if (IS_ERR(ctrl->regulator)) {  
  107. 1339         fimc_err("%s: failed to get resource %s\n",  
  108. 1340                 __func__, "s3c-fimc");  
  109. 1341         return PTR_ERR(ctrl->regulator);  
  110. 1342     }  
  111. 1343  
  112. 1344     /* fimc source clock */  
  113. 1345     srclk = clk_get(&pdev->dev, pdata->srclk_name);  
  114. 1346     if (IS_ERR(srclk)) {  
  115. 1347         fimc_err("%s: failed to get source clock of fimc\n",  
  116. 1348                 __func__);  
  117. 1349         goto err_v4l2;  
  118. 1350     }  
  119. 1351   
  120. 1352     /* fimc clock */  
  121. 1353     ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);  
  122. 1354     if (IS_ERR(ctrl->clk)) {  
  123. 1355         fimc_err("%s: failed to get fimc clock source\n",  
  124. 1356             __func__);  
  125. 1357         goto err_v4l2;  
  126. 1358     }  
  127. 1359   
  128. 1360     /* set parent for mclk */  
  129. 1361     clk_set_parent(ctrl->clk, srclk);  
  130. 1362   
  131. 1363     /* set rate for mclk */  
  132. 1364     clk_set_rate(ctrl->clk, pdata->clk_rate);  
  133. 1365   
  134. 1366     /* V4L2 device-subdev registration */  
  135. 1367     ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);  
  136. 1368     if (ret) {  
  137. 1369         fimc_err("%s: v4l2 device register failed\n", __func__);  
  138. 1370         goto err_fimc;  
  139. 1371     }  
  140. 1372   
  141. 1373     /* things to initialize once */  
  142. 1374     if (!fimc_dev->initialized) {  
  143. 1375         ret = fimc_init_global(pdev);  
  144. 1376         if (ret)  
  145. 1377             goto err_v4l2;  
  146. 1378     }  
  147. 1379   
  148. 1380     /* video device register */  
  149. 1381     ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);  
  150. 1382     if (ret) {  
  151. 1383         fimc_err("%s: cannot register video driver\n", __func__);  
  152. 1384         goto err_v4l2;  
  153. 1385     }  
  154. 1386   
  155. 1387     video_set_drvdata(ctrl->vd, ctrl);  
  156. 1388   
  157. 1389     ret = device_create_file(&(pdev->dev), &dev_attr_log_level);  
  158. 1390     if (ret < 0) {  
  159. 1391         fimc_err("failed to add sysfs entries\n");  
  160. 1392         goto err_global;  
  161. 1393     }  
  162. 1394     printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);  
  163. 1395   
  164. 1396     return 0;  
  165. 1397   
  166. 1398 err_global:  
  167. 1399     video_unregister_device(ctrl->vd);  
  168. 1400   
  169. 1401 err_v4l2:  
  170. 1402     v4l2_device_unregister(&ctrl->v4l2_dev);  
  171. 1403   
  172. 1404 err_fimc:  
  173. 1405     fimc_unregister_controller(pdev);  
  174. 1406   
  175. 1407 err_alloc:  
  176. 1408     kfree(fimc_dev);  
  177. 1409     return -EINVAL;  
  178. 1410   
  179. 1411 }  
  180.   
  181. </pre>  
  182. <p><br>  
  183. </p>  
  184. <p>1333 ~ 1334 调用平台的gpio设置函数,一般来说,这个用来设置external CameraA/CameraB的输入输出<br>  
  185. </p>  
  186. <p>1344 ~ 1364 设置mclk,mclk的频率由sensor的输出图像尺寸, 如果外围sensor自身有晶振,那么CPU不需要对外提供mclk。<br>  
  187. </p>  
  188. <p>1381 ~ 1385 注册一个video device,会生成设备节点/dev/videoX</p>  
  189. <pre></pre>  

你可能感兴趣的:(Samsung,s5pv210)