和你一起終身學(xué)習(xí),這里是程序員 Android
經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識點:
一、概覽
二、核心模塊解析
三、模塊初始化
四、處理UMD CSL請求
相機驅(qū)動層–高通KMD框架詳解
一、概覽
利用了V4L2可擴展這一特性,高通在相機驅(qū)動部分實現(xiàn)了自有的一套KMD框架,該框架通過V4L2標準方法在系統(tǒng)中創(chuàng)建設(shè)備節(jié)點,將控制接口直接暴露給UMD CSL進行訪問,而其內(nèi)部主要定義了一系列核心模塊,包括CRM(Camera Request Manager),用于管理整個KMD的Session/Link的創(chuàng)建銷毀以及Request的在子設(shè)備間的流轉(zhuǎn),該模塊創(chuàng)建video0設(shè)備節(jié)點暴露關(guān)鍵接口給UMD,此外還包括了Sync模塊,主要負責(zé)了UMD/KMD之間的數(shù)據(jù)同步與傳輸,創(chuàng)建video1設(shè)備節(jié)點暴露接口給UMD進行訪問,除此之外,為了更精細化地控制一系列的硬件圖像處理模塊,包括ISP/IPE/Sensor等硬件模塊,高通也分別為各自子模塊創(chuàng)建了設(shè)備節(jié)點,進而暴露控制接口給UMD進行訪問。
其中主要目錄如下:
- cam_core/: 關(guān)于KMD核心函數(shù)的實現(xiàn)都放在這,主要包括了subdev、node、context的一些諸如創(chuàng)建/注冊/銷毀等標準方法。
- cam_req_mgr/: CRM的具體實現(xiàn),用于創(chuàng)建v4l2_device,用于管理所有的子設(shè)備,同時生成video設(shè)備節(jié)點,暴露控制接口給UMD,主要包括了Session/Link的行為管理以及Request的同步與分發(fā),此外,還創(chuàng)建了media_device,用于暴露枚舉接口給UMD來輪詢查找整個KMD的子設(shè)備。
- cam_sync/: 該部分主要實現(xiàn)了用于保持與UMD的圖像數(shù)據(jù)的同步相關(guān)業(yè)務(wù)邏輯,由于該模塊的特殊性,高通直接為其創(chuàng)建了一個單獨的video設(shè)備節(jié)點,暴露了用于同步的一些控制接口。
- cam_utils/: 一些共有方法的實現(xiàn),包括debug方法集等
- cam_smmu/: 高通自己實現(xiàn)了一套smmu api,供KMD使用
- cam_lrme/: 低分辨率運動估計模塊的驅(qū)動實現(xiàn)
- cam_fd/: 人臉識別的驅(qū)動程序
- cam_isp/: isp的驅(qū)動程序
- cam_jpeg/: 編碼器,可以通過該驅(qū)動完成jpeg的編碼工作
- cam_cdm/: camera data mover,數(shù)據(jù)移動器的驅(qū)動實現(xiàn),主要用于解析由CSL傳入的命令信息,其中包括了寄存器的設(shè)置以及圖像數(shù)據(jù)的處理等。
- cam_cpas/: 該模塊主要用于CSL獲取camera 平臺驅(qū)動信息,IPE/BPS電源控制等
- cam_icp/: image control processor ,圖像處理控制器驅(qū)動實現(xiàn)
- cam_sensor_module/: 類傳感器的系列硬件模塊
- cam_actuator/: 對焦馬達的驅(qū)動實現(xiàn)
- cam_cci/: 實現(xiàn)了用于通訊的CCI接口,其中包括了I2C以及gpio的實現(xiàn)
- cam_csiphy: 基于MIPI CSI接口的物理層驅(qū)動,用于傳輸圖像數(shù)據(jù)
- cam_sensor_io: 使用cam_cci,向上實現(xiàn)了控制sensor的IO接口
- cam_sensor: sensor 的驅(qū)動實現(xiàn)
- cam_sensor_util: sensor相關(guān)的公有方法的實現(xiàn)
- cam_eeprom: eeprom設(shè)備的驅(qū)動實現(xiàn)
- cam_ois : 光學(xué)防抖設(shè)備的驅(qū)動實現(xiàn)
- cam_flash: 閃光燈設(shè)備的驅(qū)動實現(xiàn)
二、核心模塊解析
正如之前介紹的那樣,整個框架主要由三個部分組成,CRM/Camera Sync以及子模塊,接下來我們以下圖為例簡單講解下各自的關(guān)系:
在系統(tǒng)初始化時,CRM內(nèi)部會創(chuàng)建一個v4l2_device結(jié)構(gòu)體,用于管理所有的子設(shè)備,與此同時每一個子設(shè)備在注冊的時候都會創(chuàng)建各自的v4l2_subdev掛載到該v4l2_device上面。此外,CRM會創(chuàng)建一個video0設(shè)備節(jié)點提供關(guān)鍵接口給CSL來進行訪問,而每個子設(shè)備也會在系統(tǒng)中生成各自的v4l2-sbudev設(shè)備節(jié)點,提供接口給CSL進行更為精細化的控制。而其中的Cam Sync在初始化的過程中,也創(chuàng)建了一個v4l2_device設(shè)備,并且生成了video1節(jié)點給CSL進行控制。這個框架主要就是圍繞這三個部分進行的,CRM用于管理Session/Link的創(chuàng)建,控制Request在各個子設(shè)備中的流轉(zhuǎn),子設(shè)備受CSL控制進行配置以及圖像處理工作,而一旦圖像處理完成便會將結(jié)果發(fā)送至Cam Sync模塊,進上傳至CSL中。
1. CRM(Camera Request Manager)
該模塊本質(zhì)上是一個軟件模塊,主要做了以下幾個事情:
- 接收來自CSL的Session/Link/Request請求,并且維護其在內(nèi)核的狀態(tài)。
- 在不同pipeline delay的子模塊間,同步每一個Request狀態(tài),并按照需要發(fā)送給每一個子設(shè)備。
- 如果出現(xiàn)錯誤,負責(zé)上傳至CSL。
- 負責(zé)針對實時子模塊的flush操作。
其中針對Session/Link/Request的請求便是通過之前創(chuàng)建的video設(shè)備節(jié)點將接口暴露給CSL,一旦接收到命令便開始進行處理,而命令主要有以下幾個:
- CAM_REQ_MGR_CREATE_SESSION/CAM_REQ_MGR_DESTROY_SESSION: 分別表示了Session的創(chuàng)建和銷毀,該Session保持著與CamX-CHI的一一對應(yīng)關(guān)系。
- CAM_REQ_MGR_LINK/CAM_REQ_MGR_UNLINK: 分別表示了Link的創(chuàng)建和銷毀動作,每一個Session可以包含多條Link,而每一個Link都連接著此次圖像采集過程中所需要的子設(shè)備,CRM也是通過該Link來管理Request同步與分發(fā)的操作。
- CAM_REQ_MGR_SCHED_REQ:一旦CSL開始下發(fā)Request的時候,便可以通過該命令告知KMD,而在KMD中,CRM會將此次Request存入Link中的in_q數(shù)組中,當子設(shè)備告知準備好了此次Request的處理后,便通知子設(shè)備進行配置并處理Request。
- CAM_REQ_MGR_ALLOC_BUF/CAM_REQ_MGR_RELEASE_BUF: 圖像緩沖區(qū)的申請與釋放,CRM中使用cam_mem_table結(jié)構(gòu)體來管理著申請的緩沖區(qū)。
一旦CRM接收了來自CSL的請求,便會在內(nèi)部進行處理,而其中的一系列業(yè)務(wù)處理便會通過接下來的幾個結(jié)構(gòu)體來完成:
首先在初始化過程中,會去創(chuàng)建一個cam_req_mgr_device。
該結(jié)構(gòu)體有以下幾個主要的成員:
- video: 存儲著對應(yīng)的video_device。
- v4l2_dev: 保存著初始化過程中創(chuàng)建的v4l2_device。
- subdev_nodes_created: 標志著從屬于v4l2_device的子設(shè)備是否都成功創(chuàng)建了設(shè)備節(jié)點。
- cam_eventq: v4l2文件描述結(jié)構(gòu)體,其中維護著event事件隊列。
之后會去創(chuàng)建一個cam_req_mgr_core_device,該結(jié)構(gòu)體比較簡單主要用于維護一個Session鏈表,在CSL下發(fā)創(chuàng)建Session的動作后,會將創(chuàng)建好的Session放入該量表中,同時通過crm_lock保持著業(yè)務(wù)處理中的同步。
一個Session可以包含很多條Link,其中變量num_links存儲了Link數(shù)量,數(shù)組links存儲著所有l(wèi)ink,entry變量作為當前session的實體可以嵌入cam_req_mgr_core_device中的session鏈表中進行統(tǒng)一管理。
在CSL下發(fā)CAM_REQ_MGR_LINK命令的時候,會去創(chuàng)建cam_req_mgr_core_link。
該結(jié)構(gòu)體比較復(fù)雜,接下來我們主要介紹下幾個主要的變量:
- link_hdl:作為該Link的句柄,區(qū)別于其它Link。
- num_devs: 表示了該條Link上連接了多少個子設(shè)備。
- max_delay: 表示了從屬于該Link上的所有子設(shè)備具有的最大的Pipeline delay值。
- l_dev: 存儲著所有從屬于該Link上的子設(shè)備,后續(xù)對于子設(shè)備的控制都是通過該數(shù)組來進行的。
- req: 該成員主要用于管理下發(fā)的request。
- state: 標志著該Link的狀態(tài),而Link狀態(tài)主要包括了CAM_CRM_LINK_STATE_AVAILABLE/CAM_CRM_LINK_STATE_IDLE/CAM_CRM_LINK_STATE_READY/CAM_CRM_LINK_STATE_ERR幾種狀態(tài)。
創(chuàng)建完Link之后,會將其存入一個存儲cam_req_mgr_core_link的全局變量g_links中進行統(tǒng)一管理。
而當下發(fā)CAM_REQ_MGR_SCHED_REQ命令的時候,會在內(nèi)部進行解析,并且將其存入cam_req_mgr_core_link中的cam_req_mgr_req_data中等待后續(xù)的流轉(zhuǎn)。
其中in_q變量主要用于存儲request,而l_tbl用于記錄pipeline delay的相關(guān)信息,而apply_data數(shù)組用于存儲所有的等待處理的request信息。
2. Cam Sync
該模塊本質(zhì)上是一個軟件模塊,用于保持與UMD的圖像數(shù)據(jù)的同步,主要利用了V4L2框架的event機制,由CSL進行事件的等待,一旦數(shù)據(jù)處理完畢,該模塊便可以向上層發(fā)送事件,進而,通知CSL取出數(shù)據(jù)進行下一步處理,其中包括了幾個主要ioctl的命令:
- CAM_SYNC_CREATE: 一旦CSL部分需要創(chuàng)建一個用于同步的實體的時候便下發(fā)該命令,而在Cam Sync中,會將傳入的信息存入內(nèi)部的sync_table_row數(shù)組中進行管理,并且將生成的sync_obj傳入上層。
- CAM_SYNC_DESTROY: 銷毀用于同步的sync實體。
- CAM_SYNC_REGISTER_PAYLOAD: 通過該命令將一些同步的回調(diào)方法注冊到Cam Sync中,這樣一當數(shù)據(jù)處理完成,Cam Sync便可以由之前創(chuàng)建的sync_obj來找到相應(yīng)的回調(diào)方法,進而調(diào)用該回調(diào)方法進行后續(xù)處理。
- CAM_SYNC_DEREGISTER_PAYLOAD:釋放之前注冊的相關(guān)同步實體的信息,包括其回調(diào)方法。
- CAM_SYNC_SIGNAL:該命令主要用于CamX-CHI中軟件Node處理完數(shù)據(jù)之后,通知Cam Sync進行后續(xù)處理的目的。
其中包括了幾個比較重要的結(jié)構(gòu)體,首先在初始化過程中會去創(chuàng)建sync_device結(jié)構(gòu)體,其主要的幾個變量如下:
- vdev: 創(chuàng)建的video_device。
- v4l2_dev: 創(chuàng)建的v4l2_device設(shè)備。
- sync_table: 用于存儲sync_table_row的數(shù)組。
- cam_sync_eventq: v4l2設(shè)備描述符結(jié)構(gòu)體,其中維護著event事件隊列。
其中最重要的時sync_table中存儲的sync_table_row結(jié)構(gòu)體,它代表了整個對應(yīng)于CSL中的sync object,其中比較重要的變量含義如下:
- sync_id:該sync object的唯一標識,同時該標識于CSL保持同步。
- state: 代表了當前sync object的狀態(tài)。
- user_payload_list: 存儲著該sync object所對應(yīng)的來自UMD的payload,該payload在KMD中并沒有被使用,僅僅存儲與KMD中,一旦當前sync object被觸發(fā),便直接將其再次傳入UMD中。
三、模塊初始化
在系統(tǒng)啟動初期,整個相機驅(qū)動中的各個模塊都開始進行加載了,接下來我們依次介紹下:
首先是CRM的初始化,按照linux驅(qū)動模塊的標準方法,會走到module_init宏聲明的驅(qū)動結(jié)構(gòu)體中的probe方法,這里是cam_req_mgr_probe方法,在該方法中主要做了以下幾個事情:
- 調(diào)用cam_v4l2_device_setup方法,創(chuàng)建并向系統(tǒng)注冊用于管理所有子設(shè)備的v4l2_device。
- 調(diào)用cam_media_device_setup方法,創(chuàng)建并向系統(tǒng)注冊media_device,并且創(chuàng)建了media設(shè)備節(jié)點,用于CSL枚舉KMD中所有設(shè)備。
- 調(diào)用cam_video_device_setup方法,創(chuàng)建video_device,并將v4l2_device嵌入到該結(jié)構(gòu)體中,緊接著,使用標準的video注冊方法,創(chuàng)建了video0設(shè)備節(jié)點,其中將g_cam_ioctl_ops方法集作為了video0的擴展方法,CSL下發(fā)的有關(guān)Session/Link/Request的諸多操作都是通過該方法集來進行分發(fā)的,最后將video0 media_entity中的function賦值CAM_VNODE_DEVICE_TYPE,這樣CSL便可以通過該function判斷出該節(jié)點便是CRM了。
- 調(diào)用cam_req_mgr_util_init方法,其中初始化了一個cam_req_mgr_util_hdl_tbl,該結(jié)構(gòu)體中存在一個handle數(shù)組,而每一個handle主要用于存儲Session、Link以及各個子設(shè)備的相關(guān)信息,后期在整個圖像采集的過程中,都是通過該結(jié)構(gòu)體來找對應(yīng)的操作實體,進而采取相應(yīng)的動作。
- 調(diào)用cam_req_mgr_core_device_init方法,該方法中,會去創(chuàng)建并初始化一個cam_req_mgr_core_device結(jié)構(gòu)體,作為全局變量g_crm_core_dev存在于整個框架中,而該結(jié)構(gòu)體中主要包含了用于存儲創(chuàng)建的Session的session_head鏈表,以及用于保護Session臨界資源的crm_lock。
其次,是Cam Sync的初始化,整個流程最終會走到驅(qū)動結(jié)構(gòu)體中的probe方法中,這里是cam_sync_probe方法,在該方法中主要做了以下幾個事情:
- 創(chuàng)建sync_dev結(jié)構(gòu)體,該結(jié)構(gòu)中通過一個sync_table_row數(shù)組來維護著所有的sync objects。
- 調(diào)用cam_sync_media_controller_init方法,用于創(chuàng)建media_deivce設(shè)備,并且創(chuàng)建了media設(shè)備節(jié)點,提供給CSL枚舉子設(shè)備的能力。
- 調(diào)用v4l2_device_register方法,創(chuàng)建并像系統(tǒng)注冊一個v4l2_device結(jié)構(gòu)體,其中用于ioctl的方法集是指向的g_cam_sync_ioctl_ops,一旦CSL有創(chuàng)建/注冊sync objects需求的時候,便會最終走到該方法中,從而實現(xiàn)相應(yīng)的功能。
- 調(diào)用video_register_device方法,生成video1設(shè)備節(jié)點,暴露控制接口給CSL。
- 調(diào)用cam_sync_init_entity方法,將video1中的meida_entity中function字段賦值CAM_SYNC_DEVICE_TYPE,這樣在UMD就可以通過相應(yīng)的media節(jié)點枚舉出該模塊。
以上兩個模塊都是具有獨立的video設(shè)備節(jié)點的,但是對于子設(shè)備而言,由于代表著相應(yīng)的硬件設(shè)備,同時需要嵌入到整個框架中才能正常運行,所以高通將其抽象成了v4l2_subdev來進行管理,這里主要還是介紹兩個比較有代表性的子模塊,ISP以及Sensor。
首先來看下ISP的初始化階段,在其相應(yīng)的probe方法cam_isp_dev_probe中做了如下幾個事情:
- 調(diào)用cam_subdev_probe方法,在該方法中,會去注冊一個v4l2_subdev,并且將其掛載到CRM中的v4l2_device上,同時還創(chuàng)建了一個node,并且存入了v4l2_subdev中的token中,方便以后進行讀取,另外,將方法集賦值為cam_subdev_ops,最后,創(chuàng)建了該v4l2_subdev內(nèi)部的media_entity, 并且為其function字段賦值為CAM_IFE_DEVICE_TYPE,這樣也方便在枚舉子設(shè)備時分辨出當前節(jié)點代表著isp模塊。
- 調(diào)用cam_isp_hw_mgr_init方法,該方法用于初始化isp中的硬件模塊。
- 調(diào)用cam_isp_context_init方法,該方法中會初始化node,在node內(nèi)部創(chuàng)建一定數(shù)量的context,用于后期的狀態(tài)維護,并且為每一個context都配置了狀態(tài)機,以及子狀態(tài)機來用于管理整個isp模塊。
其次來看下Sensor模塊的初始化,在其相應(yīng)的probe方法cam_sensor_driver_i2c_probe中主要做了以下幾個事情:
- 調(diào)用cam_sensor_parse_dt方法獲取dts中定義的硬件信息。
- 調(diào)用cam_sensor_init_subdev_params方法,該方法中會創(chuàng)建v4l2_subdev,然后掛載到CRM中的v4l2_device中,并且將sensor的私有方法集cam_sensor_internal_ops賦值給v4l2_subdev結(jié)構(gòu)體中的ops,這樣一旦操作相應(yīng)的子設(shè)備節(jié)點,便最終會走到該方法集中,關(guān)于Sensor的一些操作便可以放到這個里面進行處理。最終將創(chuàng)建的v4l2_subdev中的media_entity中functon賦值為CAM_SENSOR_DEVICE_TYPE,方便CSL進行枚舉Sensor設(shè)備。
通過上面的兩個子設(shè)備的初始化代碼梳理,不難發(fā)現(xiàn),并沒有進行設(shè)備節(jié)點的創(chuàng)建,那關(guān)于節(jié)點的創(chuàng)建動作發(fā)生在哪一個階段呢? 為了解決這個疑問我們不得不先介紹下linux兩個宏定義,一個是module_init,另一個便是late_initcall,兩者都是為了聲明初始化函數(shù),但是執(zhí)行時間有一個先后順序,而late_initcall一般在所有module_init定義的方法都運行完成之后才會被運行,而針對所有子設(shè)備的節(jié)點的創(chuàng)建便是在這里完成的,在該方法中主要做了以下工作:
- 調(diào)用cam_dev_mgr_create_subdev_nodes方法,而在該方法中會去調(diào)用v4l2標準方法v4l2_device_register_subdev_nodes來統(tǒng)一創(chuàng)建掛載在CRM中v4l2_device下的子設(shè)備節(jié)點。
至此,整個KMD框架便初始化完成,現(xiàn)在便靜靜等待CSL下發(fā)請求。
四、處理UMD CSL請求
整個KMD的初始化動作在linux內(nèi)核啟動的時候完成的,要稍早于CamX-CHI整個框架的初始化,所以在CamX-CHI進行初始化的時候,KMD框架的各個資源節(jié)點都已準備妥當,接下來我們就以CamX-CHI的初始化開始詳細描述下整個KMD處理來自CSL請求的流程。
1. 獲取模塊資源
在CamX-CHI初始化的時候,并不知道內(nèi)核驅(qū)動部分是個什么狀態(tài),所以需要打開所有的media設(shè)備節(jié)點來枚舉查詢每一個驅(qū)動模塊。
首先,打開media0,根據(jù)CAM_VNODE_DEVICE_TYPE信枚舉并找到KMD框架中的CRM模塊,并調(diào)用標準open方法來打開該設(shè)備,該動作最終會調(diào)用到cam_req_mgr_open方法,該方法主要做了以下幾個工作:
- 調(diào)用v4l2_fh_open方法,打開v4l2文件。
- 調(diào)用cam_mem_mgr_init方法,初始化了內(nèi)存管理模塊,為之后的緩沖區(qū)的申請與釋放做好準備。
- 更新CRM狀態(tài)為CAM_MEM_MGR_INITIALIZED。
在打開video0之后,會另起一個線程用于監(jiān)聽video的事件,這樣就建立了與底層的雙向通訊,而在此之前,需要通過ioctl方法將CSL需要監(jiān)聽的事件下發(fā)到驅(qū)動層,其中包括以下幾個事件:
- V4L_EVENT_CAM_REQ_MGR_SOF/V4L_EVENT_CAM_REQ_MGR_SOF_BOOT_TS: 一旦底層產(chǎn)生的SOF事件,便會向CSL發(fā)送該事件。
- V4L_EVENT_CAM_REQ_MGR_ERROR: 一旦底層產(chǎn)生了錯誤,會向上拋出該事件。
一旦CSL獲取了CRM模塊信息成功之后,便開始枚舉查找各個子模塊了,其中會先去打開Sensor子設(shè)備,獲取硬件信息,并且存入CSL中,然后再依次獲取其它諸如IFE/IPE等硬件子模塊并獲取各自的信息,并存入CSL中,為之后的數(shù)據(jù)流轉(zhuǎn)做好準備。
以上動作都完成之后,便開始查詢Cam Sync模塊了,基本流程與CRM大致相同:
- 調(diào)用open方法打開video1,該方法最終會調(diào)用內(nèi)核部分的cam_sync_open方法,而該方法中會調(diào)用v4l2_fh_open方法,從而打開v4l2文件。
- 調(diào)用ioctl方法,訂閱針對CAM_SYNC_V4L_EVENT_ID_CB_TRIG事件的監(jiān)聽 ,而對于該事件,一般是在子模塊處理數(shù)據(jù)完成之后,會觸發(fā)Cam Sync發(fā)送該事件至上層。
2. 打開Session
好了,到這里,整個CamX初始化過程對于底層的請求都已經(jīng)完成了,一旦用戶打開相機應(yīng)用之后,經(jīng)過層層調(diào)用最終會去打開Session,進而調(diào)用video0的相應(yīng)的ioctl方法傳入CAM_REQ_MGR_CREATE_SESSION命令開始在驅(qū)動層打開Session的操作,而在驅(qū)動部分,會調(diào)用到CRM中的cam_req_mgr_create_session方法,在該方法中,會去創(chuàng)建一個用于代表session的handle,并將其存入全局靜態(tài)變量hdl_tbl中。緊接著會去初始化該session中的link,其中該session管理著兩個link數(shù)組,一個是用于初始化的links_init數(shù)組,一個是用于運行起來之后使用的links數(shù)組,這里的會首先初始化所有的links_init中的link,在使用的時候,會從該數(shù)組去取出一個空閑的link放入links中進行管理。
3. 打開設(shè)備
在打開Session之后,隨著Pipeline的創(chuàng)建,CamX會通過調(diào)用CSL中的相應(yīng)Node的ioctl方法,下發(fā)CAM_ACQUIRE_DEV命令,來依次打開底層硬件設(shè)備,這里我們還是以ISP為例進行分析:
- 一旦CSL調(diào)用了ISP設(shè)備節(jié)點的ioctl并且下發(fā)了CAM_ACQUIRE_DEV命令,并會通過層層調(diào)用一直調(diào)到__cam_node_handle_acquire_dev方法,在該方法中會首先去在ISP對應(yīng)的node中的存儲空閑context的隊列中獲取一個context。
- 緊接著,調(diào)用了cam_context_handle_acquire_dev方法,來通過調(diào)用之前獲取的context的對用的狀態(tài)機方法集中的acquire_dev方法來打開isp設(shè)備,而在該方法中,會調(diào)用cam_create_device_hdl方法,將當前session handle以及isp操作方法集存入存入hdl_tbl中,之后crm會通過該方法集操作isp模塊。之后會將當前isp context狀態(tài)更新為CAM_CTX_ACQUIRED,并且初始化了用于管理request的active_req_list/wati_req_list/pending_req_list/pending_req_list/free_req_list鏈表,并且將初始化好req_list都掛載到free鏈表中。
除了ISP,會根據(jù)不同的圖像采集需求,打開不同的子設(shè)備,基本流程差不多,都是通過下發(fā)CAM_ACQUIRE_DEV命令來完成的,這里我們便不進行贅述了。
4. 創(chuàng)建Link
在打開所有的子設(shè)備之后,緊接著需要將它們鏈接起來形成一個拓撲結(jié)構(gòu),方便各個子模塊的管理。而這個動作還是通過調(diào)用CRM對應(yīng)的ioctl下發(fā)CAM_REQ_MGR_LINK命令來完成的,該動作會經(jīng)過層層調(diào)用,一直調(diào)用到CRM中的cam_req_mgr_link方法,接下來我們具體介紹下該方法的主要動作:
- 調(diào)用__cam_req_mgr_reserve_link方法,在該方法中,首先會去從當前Session中的links_init數(shù)組中取出一個空閑的link,將其存入links數(shù)組,并且初始化其中的用于管理所有的request的in_q隊列。
- 調(diào)用cam_create_device_hdl,創(chuàng)建link對應(yīng)的handle,并且存入hdl_tbl中。
- 調(diào)用__cam_req_mgr_create_subdevs方法,初始化用于存儲處于當前Link中的所有子設(shè)備。
- 調(diào)用__cam_req_mgr_setup_link_info方法,該方法首先會去調(diào)用該link中的所有子設(shè)備的get_dev_info方法來獲取設(shè)備信息,然后會去依次調(diào)用hdl_tbl中的鏈接在此Link上的所有子設(shè)備的setup_link方法,來連接子設(shè)備,同時也將CRM的一些回調(diào)方法通過該方式注入到子設(shè)備中,使其具有通知CRM的能力。
- 更新該Link狀態(tài)為CAM_CRM_LINK_STATE_READY,并且創(chuàng)建了一個工作隊列用于操作的異步處理。
5. 開啟數(shù)據(jù)流
一旦整個Link創(chuàng)建完成之后,便可以開啟數(shù)據(jù)流了,該動作通過CSL控制每一個子設(shè)備來完成,這里還是以ISP為例進行分析:
由于在CamX初始化過程中已經(jīng)存有打開的ISP文件句柄,所有通過調(diào)用起iotcl方法下發(fā)CAM_START_DEV命令來通知底層ISP模塊開始進行數(shù)據(jù)流程傳輸,該命令首先會走到node,然后通過node下發(fā)到context,然后調(diào)用當前context的狀態(tài)機對應(yīng)的start_dev方法,而在該方法中,會首先更新當前context狀態(tài)為CAM_CTX_ACTIVATED,然后通過操作底層硬件管理模塊開始數(shù)據(jù)流的處理。
除了ISP,還有Sensor/FLash等模塊也是需要開啟數(shù)據(jù)流,為之后的Request的下發(fā)做好準備。
6. 下發(fā)Request
一旦開啟了整個數(shù)據(jù)處理流程,便可以接收Request請求了,而該動作依然還是通過CRM來完成,調(diào)用其ioctl方法,傳入CRM_WORKQ_TASK_SCHED_REQ命令,該動作最終會到達內(nèi)核CRM中的cam_req_mgr_schedule_request方法,而方法會將此次任務(wù)封裝成task交由工作隊列進行異步處理,而在工作隊列中最終會調(diào)用其回調(diào)方法cam_req_mgr_process_sched_req,該方法主要做了如下工作:
- 取出該request從屬的link,并且將其中的in_q取出,找到一個空閑的slot,并將該slot便作為此次request在內(nèi)核中的實體。
- 更新該slot的狀態(tài)為CRM_SLOT_STATUS_REQ_ADDED,并且將link中的open_req_cnt計數(shù)加1。
從上面的梳理不難看出,下發(fā)Request的操作并不復(fù)雜,其中并沒有一個實際的Request下發(fā)到子設(shè)備的動作,所以很自然地會產(chǎn)生一個疑問,沒有下發(fā)Request的動作,那CRM是如何來驅(qū)動整個Request的流轉(zhuǎn)的呢? 所以接下來我們來進一步介紹下,整個Request的流轉(zhuǎn)機制。
7. 子設(shè)備處理數(shù)據(jù)
當CSL下發(fā)Request到KMD之后,便會進入到DRQ中進行流轉(zhuǎn),通過之前對于CamX的學(xué)習(xí),想必大家應(yīng)該已經(jīng)熟悉了整個DRQ的運行機制,DRQ的每一個Node都會有一定依賴關(guān)系,一旦某個Node滿足依賴關(guān)系之后,便會調(diào)用其ProcessRequest開始進行此次的Request處理,而該動作會將圖像數(shù)據(jù)的以及配置信息打包,通過調(diào)用ioctl方法下發(fā)CAM_CONFIG_DEV到具體的子設(shè)備節(jié)點來將配置寫入KMD子設(shè)備中,而一旦子設(shè)備收到此次請求之后,會調(diào)用當前context的狀態(tài)機所對應(yīng)的config_dev方法,接下來我們具體介紹下其中的所作的動作:
- 將此次配置信息包括圖像數(shù)據(jù)放入硬件管理模塊中,但是此時并不進行處理,等待處理指示。
- 將此次Request信息封裝一下,通過調(diào)用之前setup_link傳入的回調(diào)方法集中的add_req方法通知CRM,而在CRM中,會首先通過一系列的判斷,如果條件滿足了便將此次request對應(yīng)的slot狀態(tài)更新為CRM_REQ_STATE_READY,并將該request存入pending隊列中。
由上面的分析,發(fā)現(xiàn)該過程中并沒有進行實際的硬件配置或者處理,此時便需要等待SOF的事件,來驅(qū)動接下來的操作,而SOF事件是ISP來通知CRM的,具體流程如下:
- EPOCH中斷產(chǎn)生,觸發(fā)回調(diào)方法__cam_isp_ctx_notify_sof_in_activated_state,在該方法中會封裝事件,并且通過調(diào)用CRM中傳入的回調(diào)方法notify_trigger將事件發(fā)送至CRM中。
- 一旦CRM收取到SOF事件,便會去找到對應(yīng)的滿足要求的request,并且調(diào)用__cam_req_mgr_process_req方法通知相應(yīng)的子設(shè)備進行配置。
- 最后ISP會將此次SOF事件通過V4L2 event機制發(fā)送至UMD,通知到CSL中。
8. 數(shù)據(jù)操作完成
當CamX中的各自Node完成了下發(fā)Request的操作之后,便會等待數(shù)據(jù)的處理完成,一旦完成便會觸發(fā)buf_done中斷,進而告知context,最終會調(diào)用cam_sync_signal方法來通知Cam Sync,而在Cam Sync中會通過子設(shè)備調(diào)用cam_sync_signal時傳入的sync_id在sync_table_row找到相應(yīng)的sync object,最終通過event機制,將此次處理完成的事件傳入UMD CSL中,進而進行后續(xù)處理。
等到最后一個Node處理完成之后,此次Request的處理便宣告完成。
之前QCamera & Mm-Camera架構(gòu)采用的相機驅(qū)動比較簡單,主要就承擔(dān)了硬件的上下電以及讀寫寄存器的任務(wù),并且控制方向都是從上到下,并且控制邏輯由UMD負責(zé)。但是隨著時代的發(fā)展,相機硬件模塊越發(fā)復(fù)雜,所以用于直接控制硬件的驅(qū)動層也需要承擔(dān)更為復(fù)雜的控制任務(wù),通過上面的分析,我們可以看到,高通重新設(shè)計了一套優(yōu)秀的KMD框架,在其中加入了更多復(fù)雜的控制邏輯,以達到精細化控制底層硬件模塊的目的,其中比較重要的是CRM對于子設(shè)備的橫向控制,這樣的好處很明顯,降低了UMD控制驅(qū)動的難度,UMD只需要將請求通過V4L2框架中的設(shè)備節(jié)點下發(fā)至KMD中,之后便由KMD中的CRM來統(tǒng)一管理,適時地將請求下發(fā)給各個子設(shè)備,進而控制著底層硬件模塊。
原文鏈接:https://blog.csdn.net/u012596975/article/details/107138655
至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請聯(lián)系小編刪除,歡迎您的建議與指正。同時期待您的關(guān)注,感謝您的閱讀,謝謝!