S5PV210開發板 攝像頭ov3640 fimc驅動分析 :fimc_dev.c 分析

大家好,今天我們分析S5PV210開發板 攝像頭ov3640 fimc驅動分析 :fimc_dev.c 分析

fimc-dev.c 是Samsung FIMC 設備的V4L2 驅動。上層應用直接操作這個設備,進行capture,圖片處理,以及overlay輸出

  1. 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。

  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的最後一個節點向前釋放。
  1. 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 }
 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進行映射


  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 }
 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中斷處理函數喚醒


  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 }
 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操作


  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 }
 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


  1. 803 void fimc_get_nv12t_size(int img_hres, int img_vres,
  2. 804 int *y_size, int *cb_size, int rotate)
  3. ...
 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影響了對齊
  1. 893 static int fimc_open(struct file *filp)
  2. 894 {
  3. ...
  4. <PRE class=cpp name="code"> 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 class=cpp name="code">#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>物理內存的使用大小 和圖片大小以及queue buffer數量成正比的,也就是說圖片分辨率Width x Height越高,queue buffer數量越多,實際使用的物理內存越大</P>
  50. <P>所以這個物理內存大小要根據項目情況具體調整,當然你也可以不調,就是浪費點內存。<BR>

  51. 1133 struct video_device fimc_video_device[FIMC_DEVICES] = {
  52. 1134 [0] = {
  53. 1135 .fops = &fimc_fops,
  54. 1136 .ioctl_ops = &fimc_v4l2_ops,
  55. 1137 .release = fimc_vdev_release,
  56. 1138 },
  57. 1139 [1] = {
  58. 1140 .fops = &fimc_fops,
  59. 1141 .ioctl_ops = &fimc_v4l2_ops,
  60. 1142 .release = fimc_vdev_release,
  61. 1143 },
  62. 1144 [2] = {
  63. 1145 .fops = &fimc_fops,
  64. 1146 .ioctl_ops = &fimc_v4l2_ops,
  65. 1147 .release = fimc_vdev_release,
  66. 1148 },
  67. 1149 };</PRE>FIMC_DEVICES 一共有三個fimc0, fimc1, fimc2設備,
  68. <P></P>
  69. <P>fops定義了設備節點的文件操作函數; ioctl_ops定義了fimc向V4L2提供的所有ioctl操作集合<BR>
  70. <BR>
  71. <BR>
  72. </P>
  73. <PRE class=html name="code">1310 static int __devinit fimc_probe(struct platform_device *pdev)
  74. 1311 {
  75. 1312 struct s3c_platform_fimc *pdata;
  76. 1313 struct fimc_control *ctrl;
  77. 1314 struct clk *srclk;
  78. 1315 int ret;
  79. 1316
  80. 1317 if (!fimc_dev) {
  81. 1318 fimc_dev = kzalloc(sizeof(*fimc_dev), GFP_KERNEL);
  82. 1319 if (!fimc_dev) {
  83. 1320 dev_err(&pdev->dev, "%s: not enough memory\n",
  84. 1321 __func__);
  85. 1322 return -ENOMEM;
  86. 1323 }
  87. 1324 }
  88. 1325
  89. 1326 ctrl = fimc_register_controller(pdev);
  90. 1327 if (!ctrl) {
  91. 1328 printk(KERN_ERR "%s: cannot register fimc\n", __func__);
  92. 1329 goto err_alloc;
  93. 1330 }
  94. 1331
  95. 1332 pdata = to_fimc_plat(&pdev->dev);
  96. 1333 if (pdata->cfg_gpio)
  97. 1334 pdata->cfg_gpio(pdev);
  98. 1335
  99. 1336 /* Get fimc power domain regulator */
  100. 1337 ctrl->regulator = regulator_get(&pdev->dev, "pd");
  101. 1338 if (IS_ERR(ctrl->regulator)) {
  102. 1339 fimc_err("%s: failed to get resource %s\n",
  103. 1340 __func__, "s3c-fimc");
  104. 1341 return PTR_ERR(ctrl->regulator);
  105. 1342 }
  106. 1343
  107. 1344 /* fimc source clock */
  108. 1345 srclk = clk_get(&pdev->dev, pdata->srclk_name);
  109. 1346 if (IS_ERR(srclk)) {
  110. 1347 fimc_err("%s: failed to get source clock of fimc\n",
  111. 1348 __func__);
  112. 1349 goto err_v4l2;
  113. 1350 }
  114. 1351
  115. 1352 /* fimc clock */
  116. 1353 ctrl->clk = clk_get(&pdev->dev, pdata->clk_name);
  117. 1354 if (IS_ERR(ctrl->clk)) {
  118. 1355 fimc_err("%s: failed to get fimc clock source\n",
  119. 1356 __func__);
  120. 1357 goto err_v4l2;
  121. 1358 }
  122. 1359
  123. 1360 /* set parent for mclk */
  124. 1361 clk_set_parent(ctrl->clk, srclk);
  125. 1362
  126. 1363 /* set rate for mclk */
  127. 1364 clk_set_rate(ctrl->clk, pdata->clk_rate);
  128. 1365
  129. 1366 /* V4L2 device-subdev registration */
  130. 1367 ret = v4l2_device_register(&pdev->dev, &ctrl->v4l2_dev);
  131. 1368 if (ret) {
  132. 1369 fimc_err("%s: v4l2 device register failed\n", __func__);
  133. 1370 goto err_fimc;
  134. 1371 }
  135. 1372
  136. 1373 /* things to initialize once */
  137. 1374 if (!fimc_dev->initialized) {
  138. 1375 ret = fimc_init_global(pdev);
  139. 1376 if (ret)
  140. 1377 goto err_v4l2;
  141. 1378 }
  142. 1379
  143. 1380 /* video device register */
  144. 1381 ret = video_register_device(ctrl->vd, VFL_TYPE_GRABBER, ctrl->id);
  145. 1382 if (ret) {
  146. 1383 fimc_err("%s: cannot register video driver\n", __func__);
  147. 1384 goto err_v4l2;
  148. 1385 }
  149. 1386
  150. 1387 video_set_drvdata(ctrl->vd, ctrl);
  151. 1388
  152. 1389 ret = device_create_file(&(pdev->dev), &dev_attr_log_level);
  153. 1390 if (ret < 0) {
  154. 1391 fimc_err("failed to add sysfs entries\n");
  155. 1392 goto err_global;
  156. 1393 }
  157. 1394 printk(KERN_INFO "FIMC%d registered successfully\n", ctrl->id);
  158. 1395
  159. 1396 return 0;
  160. 1397
  161. 1398 err_global:
  162. 1399 video_unregister_device(ctrl->vd);
  163. 1400
  164. 1401 err_v4l2:
  165. 1402 v4l2_device_unregister(&ctrl->v4l2_dev);
  166. 1403
  167. 1404 err_fimc:
  168. 1405 fimc_unregister_controller(pdev);
  169. 1406
  170. 1407 err_alloc:
  171. 1408 kfree(fimc_dev);
  172. 1409 return -EINVAL;
  173. 1410
  174. 1411 }
  175. </PRE>
  176. <P><BR>
  177. </P>
  178. <P>1333 ~ 1334 調用平臺的gpio設置函數,一般來說,這個用來設置external CameraA/CameraB的輸入輸出<BR>
  179. </P>
  180. <P>1344 ~ 1364 設置mclk,mclk的頻率由sensor的輸出圖像尺寸, 如果外圍sensor自身有晶振,那麼CPU不需要對外提供mclk。<BR>

  181. 1381 ~ 1385 註冊一個video device,會生成設備節點/dev/videoX

發佈了40 篇原創文章 · 獲贊 20 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章