V4L2 soc

轉自【http://blog.csdn.net/kickxxx/article/details/8330401

soc-camera的作者之所以引入這個子系統,我想一個主要目的就是把camera驅動劃分爲camera host端,camera device端,這樣同一個camera sensor驅動就可以方便的移植到多個camera host下,而無須做較多的改動。


本譯文取自kernel文檔:Documentation/video4linux/soc-camera.txt


術語

在閱讀本文檔前先明確幾個術語:

  • camera / camera device / camera sensor - 是一個video-camera sensor chip,可以連接到各種系統和接口,典型的使用i2c來控制和配置設備,使用並行或者串行總線來傳輸數據。
  • camera host - 一個接口,camera連接到它上面。存在於SoCs之上的特定的接口,比如PXA27X PXA3xx, SuperH, AVR32, i.MX27, i.MX31等等。我覺得i.MX51以及s5pv210物理上和這些host沒有什麼區別,只是二者的開發人員出於某種考慮而沒有使用這個子系統實現。
  • camera host bus - camera host和camera之間的連接,可以是串行的或者並行的,保存着數據線和控制線,比如clock,水平和垂直同步信號。

soc-camera subsystem的目的

soc-camera 子系統在camera host驅動和camera sensor驅動間提供了統一的接口。soc camera子系統向上提供了V4L2接口,當前僅支持open, mmap, ioctl, mmap,不支持read

這個子系統連接SoC video capture接口和CMOS camera sensor chips,使得sensor驅動可以在其他的hosts驅動上覆用。子系統在設計時也考慮到了多個camera host接口的存在,以及每個接口存在多個camera sensor,儘管在大部分情況下僅有一個camera sensor。

現存的驅動

在2.6.27-rc4中,主線內核有兩個host 驅動:PXA27x的pxa_camera.c和SuperH SoCs的sh_mobile_ceu_camera.c,有四個sensor驅動:mt9m001.c,mt9v002.c以及一個通用的soc_camera_platform.c驅動。這個列表可能不是最新的,所以在你的源碼樹中查看,可能有更多的例子存在。

實際開發中,新的sensor驅動可以參照mt9m001.c和mt9v002.c這兩個sensor驅動框架,根據項目所用的sensor稍加修改即可。

Camera host API

一個host camera driver使用soc_camera_host_register(struct soc_camera_host *)函數來註冊。host object可以如下初始化

  1. static struct soc_camera_host pxa_soc_camera_host = {  
  2.     .drv_name   = PXA_CAM_DRV_NAME,  
  3.     .ops        = &pxa_soc_camera_host_ops,  
  4. };   

所有的方法都是通過struct soc_camera_host_ops

  1. static struct soc_camera_host_ops pxa_soc_camera_host_ops = {  
  2.     .owner      = THIS_MODULE,  
  3.     .add        = pxa_camera_add_device,  
  4.     .remove     = pxa_camera_remove_device,  
  5.     .suspend    = pxa_camera_suspend,  
  6.     .resume     = pxa_camera_resume,  
  7.     .set_fmt_cap    = pxa_camera_set_fmt_cap,  
  8.     .try_fmt_cap    = pxa_camera_try_fmt_cap,  
  9.     .init_videobuf  = pxa_camera_init_videobuf,  
  10.     .reqbufs    = pxa_camera_reqbufs,  
  11.     .poll       = pxa_camera_poll,  
  12.     .querycap   = pxa_camera_querycap,  
  13.     .try_bus_param  = pxa_camera_try_bus_param,  
  14.     .set_bus_param  = pxa_camera_set_bus_param,  
  15. };  

.add是把camera sensor接入host;.remove方法從host卸載camera sensor時調用,除了執行host內部的初始化工作外,還應該調用camera sensor的.init和.release方法。(.add方法僅僅在soc_camera_open中被調用,也就是說當應用層第一個打開一個/dev/videoX設備節點時,此時進行camera host以及camera sensor的初始化是合理的)

.suspend和.resume方法實現host的電源管理功能, 他們要負責調用相應的sensor 方法。

.try_bus_param和.set_bus_param用來協商host和sensor間的物理連接參數。

.init_videobuf是當一個video-device被打開時調用,video-buffer管理的實現是完全依賴於特定的host

其餘部分被V4L2的相應部分調用。

Camera API

Sensor drivers可以使用struct soc_camera_link,典型的由platform提供,用來代表sensor連接到的host總線,這個結構還提供了.power和.reset接口。本節所描述的代碼和當前代碼已經發生了很大的變化,所以暫時略過本節。

VIDIOC_S_CROP和VIDIOC_S_FMT調用的行爲

VIDIOC_S_CROP:設置sensor窗口的位置和尺寸,單位是sensor的像素。改變sensor窗口尺寸時維持縮放因子沒變,因此用戶窗口尺寸也相應的變化了。

VIDIOC_S_FMT:設置用戶窗口。通過修改縮放因子,儘可能的保持之前設置的窗口尺寸。如果sensor窗口無法保持不變,那麼也可以修改。

soc-camera中有兩個地方,可以進行縮放和剪切:camera sensor驅動,和camera host驅動。User ioctl首先發送到host dirver,然後再向下發送給camera driver。在camera sensor驅動執行縮放和剪切可能更有效,因爲會節約camera總線帶寬以及最大幀率(如果放大還成立嗎,對於AD video芯片來說,縮放只能在host driver來做)。然而,如果camera sensor驅動無法實現要求的參數,那麼host 驅動可以決定用它自己的縮放和剪切功能來滿足用戶需求。

Camera sensor驅動soc-camera core以及host驅動交互,完全是功能上的,不涉及任何數據傳輸。因而所有的camera sensor驅動應該用當前輸出格式響應.g_fmt請求。這對於正確配置camera bus是必要的。s_fmt() 和try_fmt也不得不實現。sensor窗口和縮放因子也應該在camera sensor驅動內維護。按照V4L2 API,所有的capture驅動應該支持VIDIOC_CROPCAP ioctl,因此我們依賴於camera drivers實現.cropcap()。如果camera sensor驅動不支持cropping,那麼可以選擇不支持.s_crop,但是必須在camera host驅動實現cropping支持,起碼g_crop方法必須支持。

用戶設置的幾何尺寸保存在struct soc_camera_device的.user_width和.user_height成員中,以供soc_camera core和host drivers使用。core在成功調用s_fmt後會修改這些成員,但是如果是在其他地方修改了他們,比如s_crop,那麼host driver要負責更型他們。

  1. static LIST_HEAD(hosts)  
所有的host都在hosts指向的鏈表上

  1. static LIST_HEAD(devices);  
所有soc camera device都掛在devices指向的鏈表上。

系統內可能有多個soc_camera_host,每個soc_camera_host可以對應1 ~ n個soc_camera_device。每個soc_camera_device通過soc_camera_video_start函數創建設備節點/dev/videoX

soc_camera_host對應着系統camera處理模塊,儘管理論上可以有多個camera host,但是大部分系統僅有一個camera host

在soc_camera_host_register中調用v4l2_device_register爲這個soc_camera_host註冊一個v4l2_device設備。

在soc_camera_probe中調用soc_camera_init_i2c,爲soc_camera_device註冊一個v4l2_subdev,我們從soc_camera_probe代碼可以看出,只有i2C的設備可以使用這種方式,對於非I2C設備來說,需要soc_camera_link提供add_device來增加子設備。


soc_camera_host, soc_camera_device,v4l2_device,v4l2_subdev關係如下:

  • 理論上系統內可以有多個soc_camera_host,物理上soc_camera_host就是系統的camera處理模塊驅動
  • 一個soc_camera_host可以對應多個soc_camera_device,物理上soc_camera_device是一個camera接口,每個soc_camera_host對應一個v4l2_dev
  • 每個soc_camera_device,系統會爲他們創建設備節點/dev/videoX。
  • 每個soc_camera_device有多個v4l2_subdev,物理上v4l2_subdev可以是sensor,video AD芯片
  • v4l2_subdev可以通過i2c掛接到v4l2_device,也可以通過soc_camera_link提供的add_device來增加,這依賴於sensor和video AD芯片掛接到MCU camera接口的方式。



  1. struct soc_camera_device {  
  2.     struct list_head list;  
  3.     struct device dev;  
  4.     struct device *pdev;        /* Platform device */  
  5.     s32 user_width;  
  6.     s32 user_height;  
  7.     enum v4l2_colorspace colorspace;  
  8.     unsigned char iface;        /* Host number */  
  9.     unsigned char devnum;       /* Device number per host */  
  10.     struct soc_camera_sense *sense; /* See comment in struct definition */  
  11.     struct soc_camera_ops *ops;  
  12.     struct video_device *vdev;  
  13.     const struct soc_camera_format_xlate *current_fmt;  
  14.     struct soc_camera_format_xlate *user_formats;  
  15.     int num_user_formats;  
  16.     enum v4l2_field field;      /* Preserve field over close() */  
  17.     void *host_priv;        /* Per-device host private data */  
  18.     /* soc_camera.c private count. Only accessed with .video_lock held */  
  19.     int use_count;  
  20.     struct mutex video_lock;    /* Protects device data */  
  21.     struct file *streamer;      /* stream owner */  
  22.     struct videobuf_queue vb_vidq;  
  23. };  

每一個soc_camera_device都會對應一個/dev/videoX設備節點,除非在soc_camera_probe時失敗。

@list:soc_came_device通過這個成員連接到全局devices鏈表上

@pdev:每一個soc_camera_device,都會有一個platform device,pdev就是這個platform device結構的dev成員

@user_width, @user_height:這個camera的缺省width和height

@iface:camera bus id,也是host id,一個host可以對應多個soc_camera_device

@devnum:device number,每個soc_camera_device都會自動分配一個device number

@ops:操作集合,sensor,video AD(也許還有host)驅動要實現這個接口

@use_count:soc camera使用計數,每次打開這個設備加1,關閉則減1


soc_camera_host

  1. struct soc_camera_host {  
  2.     struct v4l2_device v4l2_dev;  
  3.     struct list_head list;  
  4.     unsigned char nr;               /* Host number */  
  5.     void *priv;  
  6.     const char *drv_name;  
  7.     struct soc_camera_host_ops *ops;  
  8. }  

@v4l2_dev:每個host都是一個v4l2_device

@list:soc_camera_host通過這個成員鏈接到全局hosts鏈表上

@nr:host number,每個host接口對應一個host

@priv:一般存放平臺特定的camera參數,比如irq,DMA


  1. struct soc_camera_ops {  
  2.     int (*suspend)(struct soc_camera_device *, pm_message_t state);  
  3.     int (*resume)(struct soc_camera_device *);  
  4.     unsigned long (*query_bus_param)(struct soc_camera_device *);  
  5.     int (*set_bus_param)(struct soc_camera_device *, unsigned long);  
  6.     int (*enum_input)(struct soc_camera_device *, struct v4l2_input *);  
  7.     const struct v4l2_queryctrl *controls;  
  8.     int num_controls;  
  9. };    
這個是由sensor,video AD或者host實現的回調函數集。

@suspend,@resume:系統休眠喚醒時的回調函數,如果不考慮電源管理,可以不實現這兩個函數

@query_bus_param:獲取sensor和host之間的總線信息,比如HSYNC VSYNC極性,數據總線寬度,數據線極性等

@set_bus_param:設置sensor和host之間的總線參數。

@enum_input:枚舉給定的input number,上層通過@v4l2_input->index指定要查看哪個input,一般可以不實現。

@controls:controls

@num_controls:@controls數目

soc_camera.c

  1. 1455 static struct platform_driver __refdata soc_camera_pdrv = {  
  2. 1456     .remove  = __devexit_p(soc_camera_pdrv_remove),  
  3. 1457     .driver  = {  
  4. 1458         .name   = "soc-camera-pdrv",  
  5. 1459         .owner  = THIS_MODULE,  
  6. 1460     },  
  7. 1461 };  
  8. 1462   
  9. 1463 static int __init soc_camera_init(void)  
  10. 1464 {  
  11. 1465     int ret = bus_register(&soc_camera_bus_type);  
  12. 1466     if (ret)  
  13. 1467         return ret;  
  14. 1468     ret = driver_register(&ic_drv);  
  15. 1469     if (ret)  
  16. 1470         goto edrvr;  
  17. 1471   
  18. 1472     ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);  
  19. 1473     if (ret)  
  20. 1474         goto epdr;  
  21. 1475   
  22. 1476     return 0;  
  23. 1477   
  24. 1478 epdr:  
  25. 1479     driver_unregister(&ic_drv);  
  26. 1480 edrvr:  
  27. 1481     bus_unregister(&soc_camera_bus_type);  
  28. 1482     return ret;  
  29. 1483 }  
1472 platform_driver_probe和platform_driver_register的區別:前者功能上和platform_driver_register是一樣的,但是在內核啓動完成後,這個函數就不能再執行了,這樣可以釋放函數soc_camera_pdrv_probe所佔的空間。

soc_camera_pdrv_probe會probe系統內名稱爲"soc-camera-pdrv"的平臺設備,系統內有幾個這樣的平臺設備,那麼就會創建幾個soc_camera_device。這些平臺設備可如下定義:

  1. struct platform_device your_mach_cameras[] = {  
  2.     {  
  3.         .name = "soc-camera-pdrv",  
  4.         .id = 0,  
  5.         .dev = {  
  6.             .platform_data = adv7180_link,  
  7.         },  
  8.     }, {  
  9.         .name = "soc-camera-pdrv",  
  10.         .id = 1,  
  11.         .dev = {  
  12.             .platform_data = tw9912_link,  
  13.         },  
  14.     }  
  15. };  

注意,這裏假定系統的camera處理模塊,接了兩個camera sensor, adv7180_link和tw9912_link

  1. static struct i2c_board_info decoder_i2c_adv7180 = {  
  2.     I2C_BOARD_INFO("adv7180", (0x42 >> 1)),   
  3. };    
  4. struct soc_camera_link adv7180_link = {  
  5.     .bus_id     = 0,  
  6.     .board_info = &decoder_i2c_adv7180,  
  7.     .i2c_adapter_id = 0,  
  8. };    
soc_camera_link主要用來定義i2c地址,如果sensor不是通過i2c連接到host上,那麼要定義add_device和del_device函數

1465 註冊一條新的總線soc-camera,這樣在scan_add_host中調用device_register時,就會把這個設備掛到這個總線上。


  1. 1135 struct bus_type soc_camera_bus_type = {  
  2. 1136     .name       = "soc-camera",  
  3. 1137     .probe      = soc_camera_probe,  
  4. 1138     .remove     = soc_camera_remove,  
  5. 1139     .suspend    = soc_camera_suspend,  
  6. 1140     .resume     = soc_camera_resume,  
  7. 1141 };  
  8. 1142 EXPORT_SYMBOL_GPL(soc_camera_bus_type);  

當一個soc-camera-device設備通過device_register註冊設備時,就會調用soc_camera_probe函數

  1. 1402 static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)  
  2. 1403 {  
  3. 1404     struct soc_camera_link *icl = pdev->dev.platform_data;  
  4. 1405     struct soc_camera_device *icd;  
  5. 1406     int ret;  
  6. 1407   
  7. 1408     if (!icl)  
  8. 1409         return -EINVAL;  
  9. 1410   
  10. 1411     icd = kzalloc(sizeof(*icd), GFP_KERNEL);  
  11. 1412     if (!icd)  
  12. 1413         return -ENOMEM;  
  13. 1414           
  14. 1415     icd->iface = icl->bus_id;  
  15. 1416     icd->pdev = &pdev->dev;  
  16. 1417     platform_set_drvdata(pdev, icd);  
  17. 1418       
  18. 1419     ret = soc_camera_device_register(icd);  
  19. 1420     if (ret < 0)  
  20. 1421         goto escdevreg;  
  21. 1422           
  22. 1423     soc_camera_device_init(&icd->dev, icl);  
  23. 1424       
  24. 1425     icd->user_width     = DEFAULT_WIDTH;  
  25. 1426     icd->user_height    = DEFAULT_HEIGHT;  
  26. 1427       
  27. 1428     return 0;  
  28. 1429       
  29. 1430 escdevreg:  
  30. 1431     kfree(icd);  
  31. 1432       
  32. 1433     return ret;  
  33. 1434 }    

查找匹配名爲soc-camera-pdrv的platform device時,調用該函數。

1419 調用soc_camera_device_register,把這個soc_camera_device加到全局camera device鏈表@devices上,並且爲它分配設備號,做一些必要的初始化

1423 設置soc_came_device對應device的bus爲soc_camera_bus_type,這樣當我們註冊設備時,就會調用soc_camera_probe


  1. 1374 /* 
  2. 1375  * Called from soc_camera_probe() above (with .video_lock held???) 
  3. 1376  */  
  4. 1377 static int soc_camera_video_start(struct soc_camera_device *icd)  
  5. 1378 {  
  6. 1379     struct device_type *type = icd->vdev->dev.type;  
  7. 1380     int ret;  
  8. 1381   
  9. 1382     if (!icd->dev.parent)  
  10. 1383         return -ENODEV;  
  11. 1384   
  12. 1385     if (!icd->ops ||  
  13. 1386         !icd->ops->query_bus_param ||  
  14. 1387         !icd->ops->set_bus_param)  
  15. 1388         return -EINVAL;  
  16. 1389   
  17. 1390     ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);  
  18. 1391     if (ret < 0) {  
  19. 1392         dev_err(&icd->dev, "video_register_device failed: %d\n", ret);  
  20. 1393         return ret;  
  21. 1394     }  
  22. 1395   
  23. 1396     /* Restore device type, possibly set by the subdevice driver */  
  24. 1397     icd->vdev->dev.type = type;  
  25. 1398   
  26. 1399     return 0;  
  27. 1400 }  

在當前的上下文,soc_camera_video_start的調用路徑如下

soc_camera_host_register ==> scan_add_host ==> device_register ==> bus_probe_device ==> soc_camera_bus_type.probe ==> soc_camera_video_start

1390 我們可以看出,系統爲每一個soc-camera-device創建了一個video device設備節點


  1. 1352 static int video_dev_create(struct soc_camera_device *icd)  
  2. 1353 {    
  3. 1354     struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);  
  4. 1355     struct video_device *vdev = video_device_alloc();  
  5. 1356   
  6. 1357     if (!vdev)  
  7. 1358         return -ENOMEM;  
  8. 1359      
  9. 1360     strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));  
  10. 1361   
  11. 1362     vdev->parent        = &icd->dev;  
  12. 1363     vdev->current_norm  = V4L2_STD_UNKNOWN;  
  13. 1364     vdev->fops      = &soc_camera_fops;  
  14. 1365     vdev->ioctl_ops     = &soc_camera_ioctl_ops;  
  15. 1366     vdev->release       = video_device_release;  
  16. 1367     vdev->tvnorms       = V4L2_STD_UNKNOWN;  
  17. 1368   
  18. 1369     icd->vdev = vdev;  
  19. 1370   
  20. 1371     return 0;  
  21. 1372 }    

當前的上下文,video_dev_create的調用路徑如下

soc_camera_host_register ==> scan_add_host ==> device_register ==> bus_probe_device ==> soc_camera_bus_type.probe ==> soc_camera_video_start

這裏面設置了video_device的兩個非常重要的參數:soc_camera_ioctl_ops和soc_camera_fops,當user space打開video device後,所有可執行的操作,都是通過這兩個入口進行的,下面是他們的定義。

  1. 549 static struct v4l2_file_operations soc_camera_fops = {  
  2. 550     .owner      = THIS_MODULE,  
  3. 551     .open       = soc_camera_open,  
  4. 552     .release    = soc_camera_close,  
  5. 553     .unlocked_ioctl = video_ioctl2,  
  6. 554     .read       = soc_camera_read,  
  7. 555     .mmap       = soc_camera_mmap,  
  8. 556     .poll       = soc_camera_poll,  
  9. 557 };  

  1. 1321 static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {  
  2. 1322     .vidioc_querycap     = soc_camera_querycap,  
  3. 1323     .vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,  
  4. 1324     .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,  
  5. 1325     .vidioc_s_fmt_vid_cap    = soc_camera_s_fmt_vid_cap,  
  6. 1326     .vidioc_enum_input   = soc_camera_enum_input,  
  7. 1327     .vidioc_g_input      = soc_camera_g_input,  
  8. 1328     .vidioc_s_input      = soc_camera_s_input,  
  9. 1329     .vidioc_s_std        = soc_camera_s_std,  
  10. 1330     .vidioc_reqbufs      = soc_camera_reqbufs,  
  11. 1331     .vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,  
  12. 1332     .vidioc_querybuf     = soc_camera_querybuf,  
  13. 1333     .vidioc_qbuf         = soc_camera_qbuf,  
  14. 1334     .vidioc_dqbuf        = soc_camera_dqbuf,  
  15. 1335     .vidioc_streamon     = soc_camera_streamon,  
  16. 1336     .vidioc_streamoff    = soc_camera_streamoff,  
  17. 1337     .vidioc_queryctrl    = soc_camera_queryctrl,  
  18. 1338     .vidioc_g_ctrl       = soc_camera_g_ctrl,  
  19. 1339     .vidioc_s_ctrl       = soc_camera_s_ctrl,  
  20. 1340     .vidioc_cropcap      = soc_camera_cropcap,  
  21. 1341     .vidioc_g_crop       = soc_camera_g_crop,  
  22. 1342     .vidioc_s_crop       = soc_camera_s_crop,  
  23. 1343     .vidioc_g_parm       = soc_camera_g_parm,  
  24. 1344     .vidioc_s_parm       = soc_camera_s_parm,  
  25. 1345     .vidioc_g_chip_ident     = soc_camera_g_chip_ident,  
  26. 1346 #ifdef CONFIG_VIDEO_ADV_DEBUG  
  27. 1347     .vidioc_g_register   = soc_camera_g_register,  
  28. 1348     .vidioc_s_register   = soc_camera_s_register,  
  29. 1349 #endif  
  30. 1350 };  

soc_camera_ops 不支持read操作,因此如果使用了soc camera子系統,那麼應用層就無法再使用read操作獲取camera 數據,而只能選擇使用mmap方式。不支持read操作沒什麼關係,大部分camera操作都是使用mmap方式進行的。samsung的s5pv210不支持read操作,而freescale mxc系列則支持read操作獲取camera數據。

soc_camera_ioctl_ops也僅僅支持了v4l2_ioctl_ops的一個子集,這就意味着應用程序的作者需要考慮ioctl可能沒有被支持。


  1. 1281 /* Image capture device */  
  2. 1282 static int soc_camera_device_register(struct soc_camera_device *icd)  
  3. 1283 {  
  4. 1284     struct soc_camera_device *ix;  
  5. 1285     int num = -1, i;  
  6. 1286   
  7. 1287     for (i = 0; i < 256 && num < 0; i++) {  
  8. 1288         num = i;  
  9. 1289         /* Check if this index is available on this interface */  
  10. 1290         list_for_each_entry(ix, &devices, list) {  
  11. 1291             if (ix->iface == icd->iface && ix->devnum == i) {  
  12. 1292                 num = -1;  
  13. 1293                 break;  
  14. 1294             }  
  15. 1295         }  
  16. 1296     }  
  17. 1297   
  18. 1298     if (num < 0)  
  19. 1299         /* 
  20. 1300          * ok, we have 256 cameras on this host... 
  21. 1301          * man, stay reasonable... 
  22. 1302          */  
  23. 1303         return -ENOMEM;  
  24. 1304   
  25. 1305     icd->devnum     = num;  
  26. 1306     icd->use_count      = 0;  
  27. 1307     icd->host_priv      = NULL;  
  28. 1308     mutex_init(&icd->video_lock);  
  29. 1309   
  30. 1310     list_add_tail(&icd->list, &devices);  
  31. 1311   
  32. 1312     return 0;  
  33. 1313 }   

把給定的@icd加到全局soc camera device列表中

1290~1294 @devices是一個全局soc camera device列表,這段代碼相當拗口,注意1293行是break 1290這個循環

  1. 1194 int soc_camera_host_register(struct soc_camera_host *ici)  
  2. 1195 {  
  3. 1196     struct soc_camera_host *ix;  
  4. 1197     int ret;  
  5. 1198   
  6. 1199     if (!ici || !ici->ops ||  
  7. 1200         !ici->ops->try_fmt ||  
  8. 1201         !ici->ops->set_fmt ||  
  9. 1202         !ici->ops->set_bus_param ||  
  10. 1203         !ici->ops->querycap ||  
  11. 1204         !ici->ops->init_videobuf ||  
  12. 1205         !ici->ops->reqbufs ||  
  13. 1206         !ici->ops->add ||  
  14. 1207         !ici->ops->remove ||  
  15. 1208         !ici->ops->poll ||  
  16. 1209         !ici->v4l2_dev.dev)  
  17. 1210         return -EINVAL;  
  18. 1211   
  19. 1212     if (!ici->ops->set_crop)  
  20. 1213         ici->ops->set_crop = default_s_crop;  
  21. 1214     if (!ici->ops->get_crop)  
  22. 1215         ici->ops->get_crop = default_g_crop;  
  23. 1216     if (!ici->ops->cropcap)  
  24. 1217         ici->ops->cropcap = default_cropcap;  
  25. 1218     if (!ici->ops->set_parm)  
  26. 1219         ici->ops->set_parm = default_s_parm;  
  27. 1220     if (!ici->ops->get_parm)  
  28. 1221         ici->ops->get_parm = default_g_parm;  
  29. 1222   
  30. 1223     mutex_lock(&list_lock);  
  31. 1224     list_for_each_entry(ix, &hosts, list) {  
  32. 1225         if (ix->nr == ici->nr) {  
  33. 1226             ret = -EBUSY;  
  34. 1227             goto edevreg;  
  35. 1228         }  
  36. 1229     }  
  37. 1230   
  38. 1231     ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);  
  39. 1232     if (ret < 0)  
  40. 1233         goto edevreg;  
  41. 1234   
  42. 1235     list_add_tail(&ici->list, &hosts);  
  43. 1236     mutex_unlock(&list_lock);  
  44. 1237   
  45. 1238     scan_add_host(ici);  
  46. 1239   
  47. 1240     return 0;  
  48. 1241   
  49. 1242 edevreg:  
  50. 1243     mutex_unlock(&list_lock);  
  51. 1244     return ret;  
  52. 1245 }  
  53. 1246 EXPORT_SYMBOL(soc_camera_host_register);  

1231 每個camera host對應一個v4l2 device(注意不是video device),host上device對應的纔是video device

1235 @host是一個全局camera host 鏈表

1238 scan_add_host 關聯繫統內屬於這個host的video device


  1. 1135 struct bus_type soc_camera_bus_type = {  
  2. 1136     .name       = "soc-camera",  
  3. 1137     .probe      = soc_camera_probe,  
  4. 1138     .remove     = soc_camera_remove,  
  5. 1139     .suspend    = soc_camera_suspend,  
  6. 1140     .resume     = soc_camera_resume,  
  7. 1141 };  
  8. 1142 EXPORT_SYMBOL_GPL(soc_camera_bus_type);  

soc camera總線代碼,當調用device_register註冊一個新設備時,會調用probe函數

  1.  947 static int soc_camera_probe(struct device *dev)  
  2.  948 {  
  3.  949     struct soc_camera_device *icd = to_soc_camera_dev(dev);  
  4.  950     struct soc_camera_host *ici = to_soc_camera_host(dev->parent);  
  5.  951     struct soc_camera_link *icl = to_soc_camera_link(icd);  
  6.  952     struct device *control = NULL;  
  7.  953     struct v4l2_subdev *sd;  
  8.  954     struct v4l2_mbus_framefmt mf;  
  9.  955     int ret;  
  10.  956   
  11.  957     dev_info(dev, "Probing %s\n", dev_name(dev));  
  12.  958   
  13.  959     ret = regulator_bulk_get(icd->pdev, icl->num_regulators,  
  14.  960                  icl->regulators);  
  15.  961     if (ret < 0)  
  16.  962         goto ereg;  
  17.  963   
  18.  964     ret = soc_camera_power_set(icd, icl, 1);  
  19.  965     if (ret < 0)  
  20.  966         goto epower;  
  21.  967   
  22.  968     /* The camera could have been already on, try to reset */  
  23.  969     if (icl->reset)  
  24.  970         icl->reset(icd->pdev);  
  25.  971   
  26.  972     ret = ici->ops->add(icd);  
  27.  973     if (ret < 0)  
  28.  974         goto eadd;  
  29.  975   
  30.  976     /* Must have icd->vdev before registering the device */  
  31.  977     ret = video_dev_create(icd);  
  32.  978     if (ret < 0)  
  33.  979         goto evdc;  
  34.  980   
  35.  981     /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */  
  36.  982     if (icl->board_info) {  
  37.  983         ret = soc_camera_init_i2c(icd, icl);  
  38.  984         if (ret < 0) {  
  39.  985             goto eadddev;  
  40.  986         }  
  41.  987     } else if (!icl->add_device || !icl->del_device) {  
  42.  988         ret = -EINVAL;  
  43.  989         goto eadddev;  
  44.  990     } else {  
  45.  991         if (icl->module_name)  
  46.  992             ret = request_module(icl->module_name);  
  47.  993   
  48.  994         ret = icl->add_device(icl, &icd->dev);  
  49.  995         if (ret < 0)  
  50.  996             goto eadddev;  
  51.  997   
  52.  998         /* 
  53.  999          * FIXME: this is racy, have to use driver-binding notification, 
  54. 1000          * when it is available 
  55. 1001          */  
  56. 1002         control = to_soc_camera_control(icd);  
  57. 1003         if (!control || !control->driver || !dev_get_drvdata(control) ||  
  58. 1004             !try_module_get(control->driver->owner)) {  
  59. 1005             icl->del_device(icl);  
  60. 1006             goto enodrv;  
  61. 1007         }  
  62. 1008     }  
  63. 1009   
  64. 1010     /* At this point client .probe() should have run already */  
  65. 1011     ret = soc_camera_init_user_formats(icd);  
  66. 1012     if (ret < 0)  
  67. 1013         goto eiufmt;  
  68. 1014   
  69. 1015     icd->field = V4L2_FIELD_ANY;  
  70. 1016   
  71. 1017     icd->vdev->lock = &icd->video_lock;  
  72. 1018   
  73. 1019     /* 
  74. 1020      * ..._video_start() will create a device node, video_register_device() 
  75. 1021      * itself is protected against concurrent open() calls, but we also have 
  76. 1022      * to protect our data. 
  77. 1023      */  
  78. 1024     mutex_lock(&icd->video_lock);  
  79. 1025   
  80. 1026     ret = soc_camera_video_start(icd);  
  81. 1027     if (ret < 0)  
  82. 1028         goto evidstart;  
  83. 1029   
  84. 1030     /* Try to improve our guess of a reasonable window format */  
  85. 1031     sd = soc_camera_to_subdev(icd);  
  86. 1032     if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {  
  87. 1033         icd->user_width     = mf.width;  
  88. 1034         icd->user_height    = mf.height;  
  89. 1035         icd->colorspace     = mf.colorspace;  
  90. 1036         icd->field      = mf.field;  
  91. 1037     }  
  92. 1038   
  93. 1039     /* Do we have to sysfs_remove_link() before device_unregister()? */  
  94. 1040     if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,  
  95. 1041                   "control"))  
  96. 1042         dev_warn(&icd->dev, "Failed creating the control symlink\n");  
  97. 1043   
  98. 1044     ici->ops->remove(icd);  
  99. 1045   
  100. 1046     soc_camera_power_set(icd, icl, 0);  
  101. 1047   
  102. 1048     mutex_unlock(&icd->video_lock);  
  103. 1049   
  104. 1050     return 0;  
  105. 1051   
  106. 1052 evidstart:  
  107. 1053     mutex_unlock(&icd->video_lock);  
  108. 1054     soc_camera_free_user_formats(icd);  
  109. 1055 eiufmt:  
  110. 1056     if (icl->board_info) {  
  111. 1057         soc_camera_free_i2c(icd);  
  112. 1058     } else {  
  113. 1059         icl->del_device(icl);  
  114. 1060         module_put(control->driver->owner);  
  115. 1061     }  
  116. 1062 enodrv:  
  117. 1063 eadddev:  
  118. 1064     video_device_release(icd->vdev);  
  119. 1065 evdc:  
  120. 1066     ici->ops->remove(icd);  
  121. 1067 eadd:  
  122. 1068     soc_camera_power_set(icd, icl, 0);  
  123. 1069 epower:  
  124. 1070     regulator_bulk_free(icl->num_regulators, icl->regulators);  
  125. 1071 ereg:  
  126. 1072     return ret;  
  127. 1073 }  

在host-driver probe函數中調用,soc_camera_host_register ==> scan_add_host ==> device_register ==> bus_probe_device ==> soc_camera_probe

972 調用camera host驅動的add函數,比如pxa平臺的pxa_camera_add_device

977 在調用video_device_register之前,要先創建video_device

982~1008 如果是i2c camera,那麼調用soc_camera_init_i2c來初始華i2c,否則就調用add_device增加設備。soc_camera_init_i2c會調用到芯片驅動i2c_driver.probe,對於我們的項目,則是adv7180_probe

1010~1013  初始化client format,調用soc_camera_init_user_format之前,已經執行了芯片的probe函數,已經可以對芯片進一步的操作。

1026 調用soc_camera_video_start註冊一個video device

1031 每一個soc camera device都一一對應一個v4l2 subdev

1044 ~ 1046 已經獲取了soc camera device必要的信息後,調用remove_device關閉soc camera device,然後調用soc_camera_power_set關閉soc camera device的電源。


  1. 869 /* So far this function cannot fail */  
  2. 870 static void scan_add_host(struct soc_camera_host *ici)  
  3. 871 {  
  4. 872     struct soc_camera_device *icd;  
  5. 873   
  6. 874     mutex_lock(&list_lock);  
  7. 875   
  8. 876     list_for_each_entry(icd, &devices, list) {  
  9. 877         if (icd->iface == ici->nr) {  
  10. 878             int ret;  
  11. 879             icd->dev.parent = ici->v4l2_dev.dev;  
  12. 880             dev_set_name(&icd->dev, "%u-%u", icd->iface,  
  13. 881                      icd->devnum);  
  14. 882             ret = device_register(&icd->dev);  
  15. 883             if (ret < 0) {  
  16. 884                 icd->dev.parent = NULL;  
  17. 885                 dev_err(&icd->dev,  
  18. 886                     "Cannot register device: %d\n", ret);  
  19. 887             }  
  20. 888         }  
  21. 889     }  
  22. 890   
  23. 891     mutex_unlock(&list_lock);  
  24. 892 }  

這個函數只被soc_camera_host_register調用。掃描系統所有的camera device,把屬於這個camera host(參數@ici指定)的所有camera device註冊到系統中。

876 系統所有的camera device,在沒有被camera host註冊前,這些camera device僅保存在@devices鏈表中

877 比較camera device的host number是否等於這個camera host

882 device_register 註冊一個設備到系統中,這個函數會調用bus_probe_device,而bus_probe_device則會調用soc_camera_bus_type.probe,也就是soc_camera_probe。這樣soc_camera_host_register就會註冊所有屬於這個host的camera device到系統中,並且創建了相應的設備節點/dev/videoX,整個設備的註冊過程全部結束了,從現在開始,可以在設備節點/dev/videoX上調用open read write ioctl以及poll。

  1. 178 static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)  
  2. 179 {  
  3. 180     int ret;  
  4. 181     struct soc_camera_device *icd = file->private_data;  
  5. 182     struct v4l2_subdev *sd = soc_camera_to_subdev(icd);  
  6. 183   
  7. 184     /* call s_routing to select the input of camera sensor */  
  8. 185     ret = v4l2_subdev_call(sd, video, s_routing, i, 0, 0);  
  9. 186   
  10. 187     return ret;  
  11. 188 }  
185 soc_camera驅動並沒有實現這句話,而是直接  return 0,這就導致當前的soc camera子系統不支持S_INPUT接口。

  1. 384 static int soc_camera_open(struct file *file)  
  2.  385 {  
  3.  386     struct video_device *vdev = video_devdata(file);  
  4.  387     struct soc_camera_device *icd = container_of(vdev->parent,  
  5.  388                              struct soc_camera_device,  
  6.  389                              dev);  
  7.  390     struct soc_camera_link *icl = to_soc_camera_link(icd);  
  8.  391     struct soc_camera_host *ici;  
  9.  392     int ret;  
  10.  393   
  11.  394     if (!icd->ops)  
  12.  395         /* No device driver attached */  
  13.  396         return -ENODEV;  
  14.  397   
  15.  398     ici = to_soc_camera_host(icd->dev.parent);  
  16.  399   
  17.  400     if (!try_module_get(ici->ops->owner)) {  
  18.  401         dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");  
  19.  402         return -EINVAL;  
  20.  403     }  
  21.  404   
  22.  405     icd->use_count++;  
  23.  406   
  24.  407     /* Now we really have to activate the camera */  
  25.  408     if (icd->use_count == 1) {  
  26.  409         /* Restore parameters before the last close() per V4L2 API */  
  27.  410         struct v4l2_format f = {  
  28.  411             .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,  
  29.  412             .fmt.pix = {  
  30.  413                 .width      = icd->user_width,  
  31.  414                 .height     = icd->user_height,  
  32.  415                 .field      = icd->field,  
  33.  416                 .colorspace = icd->colorspace,  
  34.  417                 .pixelformat    =  
  35.  418                     icd->current_fmt->host_fmt->fourcc,  
  36.  419             },  
  37.  420         };  
  38.  421   
  39.  422         ret = soc_camera_power_set(icd, icl, 1);  
  40.  423         if (ret < 0)  
  41.  424             goto epower;  
  42.  425   
  43.  426         /* The camera could have been already on, try to reset */  
  44.  427         if (icl->reset)  
  45.  428             icl->reset(icd->pdev);  
  46.  429   
  47.  430         ret = ici->ops->add(icd);  
  48.  431         if (ret < 0) {  
  49.  432             dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);  
  50.  433             goto eiciadd;  
  51.  434         }  
  52.  435   
  53.  436         pm_runtime_enable(&icd->vdev->dev);  
  54.  437         ret = pm_runtime_resume(&icd->vdev->dev);  
  55.  438         if (ret < 0 && ret != -ENOSYS)  
  56.  439             goto eresume;  
  57.  440   
  58.  441         /* 
  59.  442          * Try to configure with default parameters. Notice: this is the 
  60.  443          * very first open, so, we cannot race against other calls, 
  61.  444          * apart from someone else calling open() simultaneously, but 
  62.  445          * .video_lock is protecting us against it. 
  63.  446          */  
  64.  447         ret = soc_camera_set_fmt(icd, &f);  
  65.  448         if (ret < 0)  
  66.  449             goto esfmt;  
  67.  450   
  68.  451         ici->ops->init_videobuf(&icd->vb_vidq, icd);  
  69.  452     }  
  70.  453   
  71.  454     file->private_data = icd;  
  72.  455     dev_dbg(&icd->dev, "camera device open\n");  
  73.  456   
  74.  457     return 0;  
  75.  458   
  76.  459     /* 
  77.  460      * First four errors are entered with the .video_lock held 
  78.  461      * and use_count == 1 
  79.  462      */  
  80.  463 esfmt:  
  81.  464     pm_runtime_disable(&icd->vdev->dev);  
  82.  465 eresume:  
  83.  466     ici->ops->remove(icd);  
  84.  467 eiciadd:  
  85.  468     soc_camera_power_set(icd, icl, 0);  
  86.  469 epower:  
  87.  470     icd->use_count--;  
  88.  471     module_put(ici->ops->owner);  
  89.  472   
  90.  473     return ret;  
  91.  474 }   

當應用通過open系統調用,打開設備節點/dev/videoX時,會調用soc_camera_open

430 ici->ops->add 不僅要執行host內部的初始化,還會調用camera sensor(參數icd指定)的init

447 配置camera sensor缺省的格式參數,從我個人理解,一切合理的fmt都應該在調用S_INPUT之後進行設置。當然,這需要應用程序編程時先調用S_INPUT再進行S_FMT。

405~408 僅在第一次打開時,纔對camera host和camera sensor做初始化操作,否則,僅僅增加引用計數。

應用層通過設備節點/dev/videoX打開video4linux devices。/dev/videoX是一個字符設備,主設備號81,次設備號: (0~63)分配給capture設備,64~127分配給radio設備,223~255分配給VBI設備,128~191分配給其他類型的。

如果驅動要註冊一個video4linux設備,那麼需要調用video_register_device函數。


利用dump_stack函數,可以方便的獲取內核函數調用關係。


open調用關係

從調用關係角度來看,open是最複雜的,因爲它不僅要執行真正的打開操作,還需要爲mmap ioctl設置操作函數。

1. Application 通過open打開設備節點/dev/videoX

2. 進入系統調用sys_open,sys_open調用do_sys_open

3. do_sys_open 調用 do_flip_open

4. do_filp_open 這個函數比較長,大部分都是路徑查找相關的代碼,這裏只需要關注do_last

5. do_last 大部分是路徑相關的代碼,只需關注finish_open

6. finish_open 調用這個函數時,已經填充好了路徑名對應的nameidata結構,調用nameidata_to_flip

7. nameidata_to_flip 調用__dentry_open

8.  __dentry_open會調用chrdev_open,在__dentry_open中有如下代碼片段

  1.     f->f_op = fops_get(inode->i_fop);  
  2.     .....  
  3.     if (!open && f->f_op)  
  4.         open = f->f_op->open;  
  5.     if (open) {  
  6.         error = open(inode, f);  
  7.         if (error)  
  8.             goto cleanup_all;  
  9.     }  

當系統進行路徑lookup過程中,會把這個設備文件對應的inode讀入到內存中,在讀取文件inode的過程中,判斷這個inode是下列類型中的哪一個:regualr,char,block,pipe。此時,會根據inode類型的不同,賦給inode->i_fop不同的操作函數

在當前的case,shmem_get_inode會調用init_special_inode初始化這個inode,由於/dev/videoX是字符設備,所以inode->i_fop = &def_chr_fops。def_chr_fops.open = chrdev_open

9. chrdev_open 根據設備節點的主次設備好在系統內查找對應的cdev對象,把cdev->ops賦給filp->f_op(這個賦值操作是很重要的步驟,它把filp和具體設備的操作函數聯繫到一起),當前的case,cdev->ops是v4l2_ops。調用filp->f_op->open就完成了打開操作,flip->f_op->open則是v4l2_open。

10. v4l2_open 中調用vdev->fops->open,對於soc camera來說,我們在調用video_register_device之前,已經把vdev->fops設置爲soc_camera_fops。vdev->fops->open也就是調用soc_camera_open

11. 繞了很大一圈,終於調用了soc_camera_open。


mmap調用關係

1. 系統調用mmap_pgoff,mmap_pgoff調用do_mmap_pgoff

2. do_mmap_pgoff,調用mmap_region

3. mmap_region 調用filp->f_op->mmap,在第一次打開文件時,open操作設置了file->f_op爲v4l2_ops,file->f_op->mmap則是v4l2_mmap

4. v4l2_mmap,v4l2_mmap調用vdev->fops->mmap,對於soc_camera來說,vdev->fops是soc_camera_ops,所以vdev->fops->mmap是soc_camera_mmap

5. soc_camera_mmap


ioctl調用關係

1. 系統調用ioctl,調用do_vfs_ioctl

2. do_vfs_ioctl ,調用vfs_ioctl

3. vfs_ioctl 代碼片段

  1. if (filp->f_op->unlocked_ioctl) {  
  2.     error = filp->f_op->unlocked_ioctl(filp, cmd, arg);  
  3.     if (error == -ENOIOCTLCMD)  
  4.         error = -EINVAL;  
  5.     goto out;  
  6. else if (filp->f_op->ioctl) {  
  7.     lock_kernel();  
  8.     error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,  
  9.                   filp, cmd, arg);  
  10.     unlock_kernel();  
  11. }  

在第一次打開文件時,已經設置了filp->f_op爲v4l2_ops。注意不同內核版本中v4l2_ops實現是不同的,可能定義了ioctl,也可能定義了unlocked_ioctl。我的kernel v4l2_ops定義如下

  1. static const struct file_operations v4l2_fops = {  
  2.     .owner = THIS_MODULE,  
  3.     .read = v4l2_read,  
  4.     .write = v4l2_write,  
  5.     .open = v4l2_open,  
  6.     .mmap = v4l2_mmap,  
  7.     .unlocked_ioctl = v4l2_ioctl,  
  8. #ifdef CONFIG_COMPAT  
  9.     .compat_ioctl = v4l2_compat_ioctl32,  
  10. #endif  
  11.     .release = v4l2_release,  
  12.     .poll = v4l2_poll,  
  13.     .llseek = no_llseek,  
  14. };  

因此filep->f_op_unlocked_ioctl是v4l2_ioctl

5. v4l2_ioctl 代碼片段如下

  1. if (vdev->fops->unlocked_ioctl) {  
  2.     if (vdev->lock && mutex_lock_interruptible(vdev->lock))  
  3.         return -ERESTARTSYS;  
  4.     if (video_is_registered(vdev))  
  5.         ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);  
  6.     if (vdev->lock)  
  7.         mutex_unlock(vdev->lock);  

soc_camera_fops.unlocked_ioctl = video_ioctl2

6. video_ioctl2 調用__video_do_ioctl

7. __video_do_ioctl,這個函數對參數做一些基本的判斷,然後調用video_device的ioctl_ops,對於soc_camera系統來說,是soc_camera_ioctl_ops

8 soc_camera_ioctl_ops實現如下

  1. static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {  
  2.     .vidioc_querycap     = soc_camera_querycap,  
  3.     .vidioc_g_fmt_vid_cap    = soc_camera_g_fmt_vid_cap,  
  4.     .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,  
  5.     .vidioc_s_fmt_vid_cap    = soc_camera_s_fmt_vid_cap,  
  6.     .vidioc_enum_input   = soc_camera_enum_input,  
  7.     .vidioc_g_input      = soc_camera_g_input,  
  8.     .vidioc_s_input      = soc_camera_s_input,  
  9.     .vidioc_s_std        = soc_camera_s_std,  
  10.     .vidioc_reqbufs      = soc_camera_reqbufs,  
  11.     .vidioc_try_fmt_vid_cap  = soc_camera_try_fmt_vid_cap,  
  12.     .vidioc_querybuf     = soc_camera_querybuf,  
  13.     .vidioc_qbuf         = soc_camera_qbuf,  
  14.     .vidioc_dqbuf        = soc_camera_dqbuf,  
  15.     .vidioc_streamon     = soc_camera_streamon,  
  16.     .vidioc_streamoff    = soc_camera_streamoff,  
  17.     .vidioc_queryctrl    = soc_camera_queryctrl,  
  18.     .vidioc_g_ctrl       = soc_camera_g_ctrl,  
  19.     .vidioc_s_ctrl       = soc_camera_s_ctrl,  
  20.     .vidioc_cropcap      = soc_camera_cropcap,  
  21.     .vidioc_g_crop       = soc_camera_g_crop,  
  22.     .vidioc_s_crop       = soc_camera_s_crop,  
  23.     .vidioc_g_parm       = soc_camera_g_parm,  
  24.     .vidioc_s_parm       = soc_camera_s_parm,  
  25.     .vidioc_g_chip_ident     = soc_camera_g_chip_ident,  
  26. #ifdef CONFIG_VIDEO_ADV_DEBUG  
  27.     .vidioc_g_register   = soc_camera_g_register,  
  28.     .vidioc_s_register   = soc_camera_s_register,  
  29. #endif  
  30. };  


圖1 soc camera 子系統 系統架構圖


Soc camera sub-system對應着drivers/media/video/下的soc_camera.c soc_camera_platform.c

Soc camera host 是host端實現,是由平臺廠商實現的,向上實現soc_camera_host_ops接口,向下操作Camera host硬件以及通過平臺特定的接口操作Soc camera device

Soc camera device 是平臺的camera device(同時也是subdev),由驅動開發者來實現v4l2_subdev_call調用的subdev 接口,同時還要爲soc camera host實現平臺特定的操作接口;向下操作camera sensor或者video AD芯片。

Camera host hardware是平臺硬件相關的,不同的平臺有不同的host硬件,比如imx51的ipu,三星s5pv210的fimc控制器等。


圖1 soc camera 子系統 系統架構圖


Soc camera sub-system對應着drivers/media/video/下的soc_camera.c soc_camera_platform.c

Soc camera host 是host端實現,是由平臺廠商實現的,向上實現soc_camera_host_ops接口,向下操作Camera host硬件以及通過平臺特定的接口操作Soc camera device

Soc camera device 是平臺的camera device(同時也是subdev),由驅動開發者來實現v4l2_subdev_call調用的subdev 接口,同時還要爲soc camera host實現平臺特定的操作接口;向下操作camera sensor或者video AD芯片。

Camera host hardware是平臺硬件相關的,不同的平臺有不同的host硬件,比如imx51的ipu,三星s5pv210的fimc控制器等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章