轉自【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可以如下初始化
- static struct soc_camera_host pxa_soc_camera_host = {
- .drv_name = PXA_CAM_DRV_NAME,
- .ops = &pxa_soc_camera_host_ops,
- };
所有的方法都是通過struct soc_camera_host_ops
- static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
- .owner = THIS_MODULE,
- .add = pxa_camera_add_device,
- .remove = pxa_camera_remove_device,
- .suspend = pxa_camera_suspend,
- .resume = pxa_camera_resume,
- .set_fmt_cap = pxa_camera_set_fmt_cap,
- .try_fmt_cap = pxa_camera_try_fmt_cap,
- .init_videobuf = pxa_camera_init_videobuf,
- .reqbufs = pxa_camera_reqbufs,
- .poll = pxa_camera_poll,
- .querycap = pxa_camera_querycap,
- .try_bus_param = pxa_camera_try_bus_param,
- .set_bus_param = pxa_camera_set_bus_param,
- };
.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要負責更型他們。
- static LIST_HEAD(hosts)
- static LIST_HEAD(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接口的方式。
- struct soc_camera_device {
- struct list_head list;
- struct device dev;
- struct device *pdev; /* Platform device */
- s32 user_width;
- s32 user_height;
- enum v4l2_colorspace colorspace;
- unsigned char iface; /* Host number */
- unsigned char devnum; /* Device number per host */
- struct soc_camera_sense *sense; /* See comment in struct definition */
- struct soc_camera_ops *ops;
- struct video_device *vdev;
- const struct soc_camera_format_xlate *current_fmt;
- struct soc_camera_format_xlate *user_formats;
- int num_user_formats;
- enum v4l2_field field; /* Preserve field over close() */
- void *host_priv; /* Per-device host private data */
- /* soc_camera.c private count. Only accessed with .video_lock held */
- int use_count;
- struct mutex video_lock; /* Protects device data */
- struct file *streamer; /* stream owner */
- struct videobuf_queue vb_vidq;
- };
每一個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
- struct soc_camera_host {
- struct v4l2_device v4l2_dev;
- struct list_head list;
- unsigned char nr; /* Host number */
- void *priv;
- const char *drv_name;
- struct soc_camera_host_ops *ops;
- }
@v4l2_dev:每個host都是一個v4l2_device
@list:soc_camera_host通過這個成員鏈接到全局hosts鏈表上
@nr:host number,每個host接口對應一個host
@priv:一般存放平臺特定的camera參數,比如irq,DMA
- struct soc_camera_ops {
- int (*suspend)(struct soc_camera_device *, pm_message_t state);
- int (*resume)(struct soc_camera_device *);
- unsigned long (*query_bus_param)(struct soc_camera_device *);
- int (*set_bus_param)(struct soc_camera_device *, unsigned long);
- int (*enum_input)(struct soc_camera_device *, struct v4l2_input *);
- const struct v4l2_queryctrl *controls;
- int num_controls;
- };
@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
- 1455 static struct platform_driver __refdata soc_camera_pdrv = {
- 1456 .remove = __devexit_p(soc_camera_pdrv_remove),
- 1457 .driver = {
- 1458 .name = "soc-camera-pdrv",
- 1459 .owner = THIS_MODULE,
- 1460 },
- 1461 };
- 1462
- 1463 static int __init soc_camera_init(void)
- 1464 {
- 1465 int ret = bus_register(&soc_camera_bus_type);
- 1466 if (ret)
- 1467 return ret;
- 1468 ret = driver_register(&ic_drv);
- 1469 if (ret)
- 1470 goto edrvr;
- 1471
- 1472 ret = platform_driver_probe(&soc_camera_pdrv, soc_camera_pdrv_probe);
- 1473 if (ret)
- 1474 goto epdr;
- 1475
- 1476 return 0;
- 1477
- 1478 epdr:
- 1479 driver_unregister(&ic_drv);
- 1480 edrvr:
- 1481 bus_unregister(&soc_camera_bus_type);
- 1482 return ret;
- 1483 }
soc_camera_pdrv_probe會probe系統內名稱爲"soc-camera-pdrv"的平臺設備,系統內有幾個這樣的平臺設備,那麼就會創建幾個soc_camera_device。這些平臺設備可如下定義:
- struct platform_device your_mach_cameras[] = {
- {
- .name = "soc-camera-pdrv",
- .id = 0,
- .dev = {
- .platform_data = adv7180_link,
- },
- }, {
- .name = "soc-camera-pdrv",
- .id = 1,
- .dev = {
- .platform_data = tw9912_link,
- },
- }
- };
注意,這裏假定系統的camera處理模塊,接了兩個camera sensor, adv7180_link和tw9912_link
- static struct i2c_board_info decoder_i2c_adv7180 = {
- I2C_BOARD_INFO("adv7180", (0x42 >> 1)),
- };
- struct soc_camera_link adv7180_link = {
- .bus_id = 0,
- .board_info = &decoder_i2c_adv7180,
- .i2c_adapter_id = 0,
- };
1465 註冊一條新的總線soc-camera,這樣在scan_add_host中調用device_register時,就會把這個設備掛到這個總線上。
- 1135 struct bus_type soc_camera_bus_type = {
- 1136 .name = "soc-camera",
- 1137 .probe = soc_camera_probe,
- 1138 .remove = soc_camera_remove,
- 1139 .suspend = soc_camera_suspend,
- 1140 .resume = soc_camera_resume,
- 1141 };
- 1142 EXPORT_SYMBOL_GPL(soc_camera_bus_type);
當一個soc-camera-device設備通過device_register註冊設備時,就會調用soc_camera_probe函數
- 1402 static int __devinit soc_camera_pdrv_probe(struct platform_device *pdev)
- 1403 {
- 1404 struct soc_camera_link *icl = pdev->dev.platform_data;
- 1405 struct soc_camera_device *icd;
- 1406 int ret;
- 1407
- 1408 if (!icl)
- 1409 return -EINVAL;
- 1410
- 1411 icd = kzalloc(sizeof(*icd), GFP_KERNEL);
- 1412 if (!icd)
- 1413 return -ENOMEM;
- 1414
- 1415 icd->iface = icl->bus_id;
- 1416 icd->pdev = &pdev->dev;
- 1417 platform_set_drvdata(pdev, icd);
- 1418
- 1419 ret = soc_camera_device_register(icd);
- 1420 if (ret < 0)
- 1421 goto escdevreg;
- 1422
- 1423 soc_camera_device_init(&icd->dev, icl);
- 1424
- 1425 icd->user_width = DEFAULT_WIDTH;
- 1426 icd->user_height = DEFAULT_HEIGHT;
- 1427
- 1428 return 0;
- 1429
- 1430 escdevreg:
- 1431 kfree(icd);
- 1432
- 1433 return ret;
- 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
- 1374 /*
- 1375 * Called from soc_camera_probe() above (with .video_lock held???)
- 1376 */
- 1377 static int soc_camera_video_start(struct soc_camera_device *icd)
- 1378 {
- 1379 struct device_type *type = icd->vdev->dev.type;
- 1380 int ret;
- 1381
- 1382 if (!icd->dev.parent)
- 1383 return -ENODEV;
- 1384
- 1385 if (!icd->ops ||
- 1386 !icd->ops->query_bus_param ||
- 1387 !icd->ops->set_bus_param)
- 1388 return -EINVAL;
- 1389
- 1390 ret = video_register_device(icd->vdev, VFL_TYPE_GRABBER, -1);
- 1391 if (ret < 0) {
- 1392 dev_err(&icd->dev, "video_register_device failed: %d\n", ret);
- 1393 return ret;
- 1394 }
- 1395
- 1396 /* Restore device type, possibly set by the subdevice driver */
- 1397 icd->vdev->dev.type = type;
- 1398
- 1399 return 0;
- 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設備節點
- 1352 static int video_dev_create(struct soc_camera_device *icd)
- 1353 {
- 1354 struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
- 1355 struct video_device *vdev = video_device_alloc();
- 1356
- 1357 if (!vdev)
- 1358 return -ENOMEM;
- 1359
- 1360 strlcpy(vdev->name, ici->drv_name, sizeof(vdev->name));
- 1361
- 1362 vdev->parent = &icd->dev;
- 1363 vdev->current_norm = V4L2_STD_UNKNOWN;
- 1364 vdev->fops = &soc_camera_fops;
- 1365 vdev->ioctl_ops = &soc_camera_ioctl_ops;
- 1366 vdev->release = video_device_release;
- 1367 vdev->tvnorms = V4L2_STD_UNKNOWN;
- 1368
- 1369 icd->vdev = vdev;
- 1370
- 1371 return 0;
- 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後,所有可執行的操作,都是通過這兩個入口進行的,下面是他們的定義。
- 549 static struct v4l2_file_operations soc_camera_fops = {
- 550 .owner = THIS_MODULE,
- 551 .open = soc_camera_open,
- 552 .release = soc_camera_close,
- 553 .unlocked_ioctl = video_ioctl2,
- 554 .read = soc_camera_read,
- 555 .mmap = soc_camera_mmap,
- 556 .poll = soc_camera_poll,
- 557 };
- 1321 static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
- 1322 .vidioc_querycap = soc_camera_querycap,
- 1323 .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
- 1324 .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
- 1325 .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
- 1326 .vidioc_enum_input = soc_camera_enum_input,
- 1327 .vidioc_g_input = soc_camera_g_input,
- 1328 .vidioc_s_input = soc_camera_s_input,
- 1329 .vidioc_s_std = soc_camera_s_std,
- 1330 .vidioc_reqbufs = soc_camera_reqbufs,
- 1331 .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
- 1332 .vidioc_querybuf = soc_camera_querybuf,
- 1333 .vidioc_qbuf = soc_camera_qbuf,
- 1334 .vidioc_dqbuf = soc_camera_dqbuf,
- 1335 .vidioc_streamon = soc_camera_streamon,
- 1336 .vidioc_streamoff = soc_camera_streamoff,
- 1337 .vidioc_queryctrl = soc_camera_queryctrl,
- 1338 .vidioc_g_ctrl = soc_camera_g_ctrl,
- 1339 .vidioc_s_ctrl = soc_camera_s_ctrl,
- 1340 .vidioc_cropcap = soc_camera_cropcap,
- 1341 .vidioc_g_crop = soc_camera_g_crop,
- 1342 .vidioc_s_crop = soc_camera_s_crop,
- 1343 .vidioc_g_parm = soc_camera_g_parm,
- 1344 .vidioc_s_parm = soc_camera_s_parm,
- 1345 .vidioc_g_chip_ident = soc_camera_g_chip_ident,
- 1346 #ifdef CONFIG_VIDEO_ADV_DEBUG
- 1347 .vidioc_g_register = soc_camera_g_register,
- 1348 .vidioc_s_register = soc_camera_s_register,
- 1349 #endif
- 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可能沒有被支持。
- 1281 /* Image capture device */
- 1282 static int soc_camera_device_register(struct soc_camera_device *icd)
- 1283 {
- 1284 struct soc_camera_device *ix;
- 1285 int num = -1, i;
- 1286
- 1287 for (i = 0; i < 256 && num < 0; i++) {
- 1288 num = i;
- 1289 /* Check if this index is available on this interface */
- 1290 list_for_each_entry(ix, &devices, list) {
- 1291 if (ix->iface == icd->iface && ix->devnum == i) {
- 1292 num = -1;
- 1293 break;
- 1294 }
- 1295 }
- 1296 }
- 1297
- 1298 if (num < 0)
- 1299 /*
- 1300 * ok, we have 256 cameras on this host...
- 1301 * man, stay reasonable...
- 1302 */
- 1303 return -ENOMEM;
- 1304
- 1305 icd->devnum = num;
- 1306 icd->use_count = 0;
- 1307 icd->host_priv = NULL;
- 1308 mutex_init(&icd->video_lock);
- 1309
- 1310 list_add_tail(&icd->list, &devices);
- 1311
- 1312 return 0;
- 1313 }
把給定的@icd加到全局soc camera device列表中
1290~1294 @devices是一個全局soc camera device列表,這段代碼相當拗口,注意1293行是break 1290這個循環
- 1194 int soc_camera_host_register(struct soc_camera_host *ici)
- 1195 {
- 1196 struct soc_camera_host *ix;
- 1197 int ret;
- 1198
- 1199 if (!ici || !ici->ops ||
- 1200 !ici->ops->try_fmt ||
- 1201 !ici->ops->set_fmt ||
- 1202 !ici->ops->set_bus_param ||
- 1203 !ici->ops->querycap ||
- 1204 !ici->ops->init_videobuf ||
- 1205 !ici->ops->reqbufs ||
- 1206 !ici->ops->add ||
- 1207 !ici->ops->remove ||
- 1208 !ici->ops->poll ||
- 1209 !ici->v4l2_dev.dev)
- 1210 return -EINVAL;
- 1211
- 1212 if (!ici->ops->set_crop)
- 1213 ici->ops->set_crop = default_s_crop;
- 1214 if (!ici->ops->get_crop)
- 1215 ici->ops->get_crop = default_g_crop;
- 1216 if (!ici->ops->cropcap)
- 1217 ici->ops->cropcap = default_cropcap;
- 1218 if (!ici->ops->set_parm)
- 1219 ici->ops->set_parm = default_s_parm;
- 1220 if (!ici->ops->get_parm)
- 1221 ici->ops->get_parm = default_g_parm;
- 1222
- 1223 mutex_lock(&list_lock);
- 1224 list_for_each_entry(ix, &hosts, list) {
- 1225 if (ix->nr == ici->nr) {
- 1226 ret = -EBUSY;
- 1227 goto edevreg;
- 1228 }
- 1229 }
- 1230
- 1231 ret = v4l2_device_register(ici->v4l2_dev.dev, &ici->v4l2_dev);
- 1232 if (ret < 0)
- 1233 goto edevreg;
- 1234
- 1235 list_add_tail(&ici->list, &hosts);
- 1236 mutex_unlock(&list_lock);
- 1237
- 1238 scan_add_host(ici);
- 1239
- 1240 return 0;
- 1241
- 1242 edevreg:
- 1243 mutex_unlock(&list_lock);
- 1244 return ret;
- 1245 }
- 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
- 1135 struct bus_type soc_camera_bus_type = {
- 1136 .name = "soc-camera",
- 1137 .probe = soc_camera_probe,
- 1138 .remove = soc_camera_remove,
- 1139 .suspend = soc_camera_suspend,
- 1140 .resume = soc_camera_resume,
- 1141 };
- 1142 EXPORT_SYMBOL_GPL(soc_camera_bus_type);
soc camera總線代碼,當調用device_register註冊一個新設備時,會調用probe函數
- 947 static int soc_camera_probe(struct device *dev)
- 948 {
- 949 struct soc_camera_device *icd = to_soc_camera_dev(dev);
- 950 struct soc_camera_host *ici = to_soc_camera_host(dev->parent);
- 951 struct soc_camera_link *icl = to_soc_camera_link(icd);
- 952 struct device *control = NULL;
- 953 struct v4l2_subdev *sd;
- 954 struct v4l2_mbus_framefmt mf;
- 955 int ret;
- 956
- 957 dev_info(dev, "Probing %s\n", dev_name(dev));
- 958
- 959 ret = regulator_bulk_get(icd->pdev, icl->num_regulators,
- 960 icl->regulators);
- 961 if (ret < 0)
- 962 goto ereg;
- 963
- 964 ret = soc_camera_power_set(icd, icl, 1);
- 965 if (ret < 0)
- 966 goto epower;
- 967
- 968 /* The camera could have been already on, try to reset */
- 969 if (icl->reset)
- 970 icl->reset(icd->pdev);
- 971
- 972 ret = ici->ops->add(icd);
- 973 if (ret < 0)
- 974 goto eadd;
- 975
- 976 /* Must have icd->vdev before registering the device */
- 977 ret = video_dev_create(icd);
- 978 if (ret < 0)
- 979 goto evdc;
- 980
- 981 /* Non-i2c cameras, e.g., soc_camera_platform, have no board_info */
- 982 if (icl->board_info) {
- 983 ret = soc_camera_init_i2c(icd, icl);
- 984 if (ret < 0) {
- 985 goto eadddev;
- 986 }
- 987 } else if (!icl->add_device || !icl->del_device) {
- 988 ret = -EINVAL;
- 989 goto eadddev;
- 990 } else {
- 991 if (icl->module_name)
- 992 ret = request_module(icl->module_name);
- 993
- 994 ret = icl->add_device(icl, &icd->dev);
- 995 if (ret < 0)
- 996 goto eadddev;
- 997
- 998 /*
- 999 * FIXME: this is racy, have to use driver-binding notification,
- 1000 * when it is available
- 1001 */
- 1002 control = to_soc_camera_control(icd);
- 1003 if (!control || !control->driver || !dev_get_drvdata(control) ||
- 1004 !try_module_get(control->driver->owner)) {
- 1005 icl->del_device(icl);
- 1006 goto enodrv;
- 1007 }
- 1008 }
- 1009
- 1010 /* At this point client .probe() should have run already */
- 1011 ret = soc_camera_init_user_formats(icd);
- 1012 if (ret < 0)
- 1013 goto eiufmt;
- 1014
- 1015 icd->field = V4L2_FIELD_ANY;
- 1016
- 1017 icd->vdev->lock = &icd->video_lock;
- 1018
- 1019 /*
- 1020 * ..._video_start() will create a device node, video_register_device()
- 1021 * itself is protected against concurrent open() calls, but we also have
- 1022 * to protect our data.
- 1023 */
- 1024 mutex_lock(&icd->video_lock);
- 1025
- 1026 ret = soc_camera_video_start(icd);
- 1027 if (ret < 0)
- 1028 goto evidstart;
- 1029
- 1030 /* Try to improve our guess of a reasonable window format */
- 1031 sd = soc_camera_to_subdev(icd);
- 1032 if (!v4l2_subdev_call(sd, video, g_mbus_fmt, &mf)) {
- 1033 icd->user_width = mf.width;
- 1034 icd->user_height = mf.height;
- 1035 icd->colorspace = mf.colorspace;
- 1036 icd->field = mf.field;
- 1037 }
- 1038
- 1039 /* Do we have to sysfs_remove_link() before device_unregister()? */
- 1040 if (sysfs_create_link(&icd->dev.kobj, &to_soc_camera_control(icd)->kobj,
- 1041 "control"))
- 1042 dev_warn(&icd->dev, "Failed creating the control symlink\n");
- 1043
- 1044 ici->ops->remove(icd);
- 1045
- 1046 soc_camera_power_set(icd, icl, 0);
- 1047
- 1048 mutex_unlock(&icd->video_lock);
- 1049
- 1050 return 0;
- 1051
- 1052 evidstart:
- 1053 mutex_unlock(&icd->video_lock);
- 1054 soc_camera_free_user_formats(icd);
- 1055 eiufmt:
- 1056 if (icl->board_info) {
- 1057 soc_camera_free_i2c(icd);
- 1058 } else {
- 1059 icl->del_device(icl);
- 1060 module_put(control->driver->owner);
- 1061 }
- 1062 enodrv:
- 1063 eadddev:
- 1064 video_device_release(icd->vdev);
- 1065 evdc:
- 1066 ici->ops->remove(icd);
- 1067 eadd:
- 1068 soc_camera_power_set(icd, icl, 0);
- 1069 epower:
- 1070 regulator_bulk_free(icl->num_regulators, icl->regulators);
- 1071 ereg:
- 1072 return ret;
- 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的電源。
- 869 /* So far this function cannot fail */
- 870 static void scan_add_host(struct soc_camera_host *ici)
- 871 {
- 872 struct soc_camera_device *icd;
- 873
- 874 mutex_lock(&list_lock);
- 875
- 876 list_for_each_entry(icd, &devices, list) {
- 877 if (icd->iface == ici->nr) {
- 878 int ret;
- 879 icd->dev.parent = ici->v4l2_dev.dev;
- 880 dev_set_name(&icd->dev, "%u-%u", icd->iface,
- 881 icd->devnum);
- 882 ret = device_register(&icd->dev);
- 883 if (ret < 0) {
- 884 icd->dev.parent = NULL;
- 885 dev_err(&icd->dev,
- 886 "Cannot register device: %d\n", ret);
- 887 }
- 888 }
- 889 }
- 890
- 891 mutex_unlock(&list_lock);
- 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。
- 178 static int soc_camera_s_input(struct file *file, void *priv, unsigned int i)
- 179 {
- 180 int ret;
- 181 struct soc_camera_device *icd = file->private_data;
- 182 struct v4l2_subdev *sd = soc_camera_to_subdev(icd);
- 183
- 184 /* call s_routing to select the input of camera sensor */
- 185 ret = v4l2_subdev_call(sd, video, s_routing, i, 0, 0);
- 186
- 187 return ret;
- 188 }
- 384 static int soc_camera_open(struct file *file)
- 385 {
- 386 struct video_device *vdev = video_devdata(file);
- 387 struct soc_camera_device *icd = container_of(vdev->parent,
- 388 struct soc_camera_device,
- 389 dev);
- 390 struct soc_camera_link *icl = to_soc_camera_link(icd);
- 391 struct soc_camera_host *ici;
- 392 int ret;
- 393
- 394 if (!icd->ops)
- 395 /* No device driver attached */
- 396 return -ENODEV;
- 397
- 398 ici = to_soc_camera_host(icd->dev.parent);
- 399
- 400 if (!try_module_get(ici->ops->owner)) {
- 401 dev_err(&icd->dev, "Couldn't lock capture bus driver.\n");
- 402 return -EINVAL;
- 403 }
- 404
- 405 icd->use_count++;
- 406
- 407 /* Now we really have to activate the camera */
- 408 if (icd->use_count == 1) {
- 409 /* Restore parameters before the last close() per V4L2 API */
- 410 struct v4l2_format f = {
- 411 .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
- 412 .fmt.pix = {
- 413 .width = icd->user_width,
- 414 .height = icd->user_height,
- 415 .field = icd->field,
- 416 .colorspace = icd->colorspace,
- 417 .pixelformat =
- 418 icd->current_fmt->host_fmt->fourcc,
- 419 },
- 420 };
- 421
- 422 ret = soc_camera_power_set(icd, icl, 1);
- 423 if (ret < 0)
- 424 goto epower;
- 425
- 426 /* The camera could have been already on, try to reset */
- 427 if (icl->reset)
- 428 icl->reset(icd->pdev);
- 429
- 430 ret = ici->ops->add(icd);
- 431 if (ret < 0) {
- 432 dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
- 433 goto eiciadd;
- 434 }
- 435
- 436 pm_runtime_enable(&icd->vdev->dev);
- 437 ret = pm_runtime_resume(&icd->vdev->dev);
- 438 if (ret < 0 && ret != -ENOSYS)
- 439 goto eresume;
- 440
- 441 /*
- 442 * Try to configure with default parameters. Notice: this is the
- 443 * very first open, so, we cannot race against other calls,
- 444 * apart from someone else calling open() simultaneously, but
- 445 * .video_lock is protecting us against it.
- 446 */
- 447 ret = soc_camera_set_fmt(icd, &f);
- 448 if (ret < 0)
- 449 goto esfmt;
- 450
- 451 ici->ops->init_videobuf(&icd->vb_vidq, icd);
- 452 }
- 453
- 454 file->private_data = icd;
- 455 dev_dbg(&icd->dev, "camera device open\n");
- 456
- 457 return 0;
- 458
- 459 /*
- 460 * First four errors are entered with the .video_lock held
- 461 * and use_count == 1
- 462 */
- 463 esfmt:
- 464 pm_runtime_disable(&icd->vdev->dev);
- 465 eresume:
- 466 ici->ops->remove(icd);
- 467 eiciadd:
- 468 soc_camera_power_set(icd, icl, 0);
- 469 epower:
- 470 icd->use_count--;
- 471 module_put(ici->ops->owner);
- 472
- 473 return ret;
- 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中有如下代碼片段
- f->f_op = fops_get(inode->i_fop);
- .....
- if (!open && f->f_op)
- open = f->f_op->open;
- if (open) {
- error = open(inode, f);
- if (error)
- goto cleanup_all;
- }
當系統進行路徑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 代碼片段
- if (filp->f_op->unlocked_ioctl) {
- error = filp->f_op->unlocked_ioctl(filp, cmd, arg);
- if (error == -ENOIOCTLCMD)
- error = -EINVAL;
- goto out;
- } else if (filp->f_op->ioctl) {
- lock_kernel();
- error = filp->f_op->ioctl(filp->f_path.dentry->d_inode,
- filp, cmd, arg);
- unlock_kernel();
- }
在第一次打開文件時,已經設置了filp->f_op爲v4l2_ops。注意不同內核版本中v4l2_ops實現是不同的,可能定義了ioctl,也可能定義了unlocked_ioctl。我的kernel v4l2_ops定義如下
- static const struct file_operations v4l2_fops = {
- .owner = THIS_MODULE,
- .read = v4l2_read,
- .write = v4l2_write,
- .open = v4l2_open,
- .mmap = v4l2_mmap,
- .unlocked_ioctl = v4l2_ioctl,
- #ifdef CONFIG_COMPAT
- .compat_ioctl = v4l2_compat_ioctl32,
- #endif
- .release = v4l2_release,
- .poll = v4l2_poll,
- .llseek = no_llseek,
- };
因此filep->f_op_unlocked_ioctl是v4l2_ioctl
5. v4l2_ioctl 代碼片段如下
- if (vdev->fops->unlocked_ioctl) {
- if (vdev->lock && mutex_lock_interruptible(vdev->lock))
- return -ERESTARTSYS;
- if (video_is_registered(vdev))
- ret = vdev->fops->unlocked_ioctl(filp, cmd, arg);
- if (vdev->lock)
- 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實現如下
- static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = {
- .vidioc_querycap = soc_camera_querycap,
- .vidioc_g_fmt_vid_cap = soc_camera_g_fmt_vid_cap,
- .vidioc_enum_fmt_vid_cap = soc_camera_enum_fmt_vid_cap,
- .vidioc_s_fmt_vid_cap = soc_camera_s_fmt_vid_cap,
- .vidioc_enum_input = soc_camera_enum_input,
- .vidioc_g_input = soc_camera_g_input,
- .vidioc_s_input = soc_camera_s_input,
- .vidioc_s_std = soc_camera_s_std,
- .vidioc_reqbufs = soc_camera_reqbufs,
- .vidioc_try_fmt_vid_cap = soc_camera_try_fmt_vid_cap,
- .vidioc_querybuf = soc_camera_querybuf,
- .vidioc_qbuf = soc_camera_qbuf,
- .vidioc_dqbuf = soc_camera_dqbuf,
- .vidioc_streamon = soc_camera_streamon,
- .vidioc_streamoff = soc_camera_streamoff,
- .vidioc_queryctrl = soc_camera_queryctrl,
- .vidioc_g_ctrl = soc_camera_g_ctrl,
- .vidioc_s_ctrl = soc_camera_s_ctrl,
- .vidioc_cropcap = soc_camera_cropcap,
- .vidioc_g_crop = soc_camera_g_crop,
- .vidioc_s_crop = soc_camera_s_crop,
- .vidioc_g_parm = soc_camera_g_parm,
- .vidioc_s_parm = soc_camera_s_parm,
- .vidioc_g_chip_ident = soc_camera_g_chip_ident,
- #ifdef CONFIG_VIDEO_ADV_DEBUG
- .vidioc_g_register = soc_camera_g_register,
- .vidioc_s_register = soc_camera_s_register,
- #endif
- };
圖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控制器等。