本文以RK3568外接GC8034為例,首先介紹MIPI CSI攝像頭的適配方法,然后介紹cmos sensor驅(qū)動的一些細節(jié)與cmos sensor驅(qū)動的工作流程。
硬件準(zhǔn)備
首先介紹一下硬件。主板為風(fēng)火輪科技的YY3568開發(fā)板,主控RK3568。
它支持一個MIPI CSI接口。此接口為4LANE,可以拆分為2個2LANE的。攝像頭模組為TSC8034-HYX5,此模組主控為GC8034。此外,模組上面有一顆DW9714,這是一顆VCM,用于控制鏡頭伸縮。模組連接到一個轉(zhuǎn)接板,然后轉(zhuǎn)接板通過排線連接到Y(jié)Y3568開發(fā)板MIPI CSI接口。
目前市面上大多數(shù)的cmos image sensor一般會包含控制接口(i2cspi等)用于寄存器讀寫,數(shù)據(jù)接口(csi/bt656等)用于傳原始圖像(raw image)數(shù)據(jù)。
軟件準(zhǔn)備
本文所有的源碼分析基于rk的4.19版本kernel。
Android和Debian用的內(nèi)核源碼是一樣的。
V4L2框架簡介
V4L2(Video for linux2)為linux中關(guān)于video設(shè)備的內(nèi)核驅(qū)動。目前RK平臺全部使用V4L2框架來操作攝像頭設(shè)備。V4L2框架的組成大致如下圖所示
V4L2里面有v4l2-subdev和v4l2_device,以及videobuf2-core三類設(shè)備。
v4l2-subdev指的是硬件上面的接口,包括sensor,以及sensor的接口如mipicsi或者bt656等。對于用戶層來說,其控制節(jié)點為/dev/v4l-subdevX。
v4l2_device指的是那種能夠向用戶層傳遞數(shù)據(jù)的設(shè)備,在rk平臺上,這個設(shè)備可以是ISP(ImageSignal Processing unit),也可以是CIF(Camera Interface)。ISP具備圖像處理功能,縮放以及壓縮功能。如果需要對圖像進行預(yù)處理,則需要用到ISP。其控制節(jié)點為 /dev/videoX
videobuf2-core用于分配和處理視頻幀緩沖區(qū),比如對mmap等操作提供支持。
Kernel部分修改
首先要配置鏈路。由于從GC8034獲取的圖像需要進行前處理才能被用戶層使用,因此需要使用ISP,將鏈路設(shè)置為GC8034-> MIPI 接口->ISP。
首先配置GC8034。板上攝像頭接口的定義如下
此處可以看出,其復(fù)位腳使用的是GPIO3_B5,電源使能腳用的是GPIO4_B5,然后使用I2C4與GC8034和DW9714通信,另外攝像頭的時鐘要由主控提供,設(shè)備樹配置如下。
&i2c4 { status = "okay"; dw9714: dw9714@c { compatible = "dongwoon,dw9714"; status = "okay"; reg = <0x0c>; rockchip,camera-module-index = <0>; rockchip,vcm-start-current = <10>; rockchip,vcm-rated-current = <85>; rockchip,vcm-step-mode = <5>; rockchip,camera-module-facing = "back"; }; gc8034: gc8034@37 { compatible = "galaxycore,gc8034"; reg = <0x37>; clocks = <&cru CLK_CIF_OUT>; clock-names = "xvclk"; power-domains = <&power RK3568_PD_VI>; //sensor mclk pinctl設(shè)置。如果sensor的工作時鐘由主控提供,則此處必須配置 pinctrl-names = "default"; pinctrl-0 = <&cif_clk>; // reset管腳分配及有效電平 reset-gpios = <&gpio3 RK_PB5 GPIO_ACTIVE_LOW>; // powerdown管腳分配及有效電平 pwdn-gpios = <&gpio4 RK_PB5 GPIO_ACTIVE_LOW>; rockchip,grf = <&grf>; // 模組編號,該編號不要重復(fù) rockchip,camera-module-index = <0>; // 模組朝向,有"back"和"front" rockchip,camera-module-facing = "back"; rockchip,camera-module-name = "RK-CMK-8M-2-v1"; rockchip,camera-module-lens-name = "CK8401"; lens-focus = <&dw9714>; port { gc8034_out: endpoint { // csi2 dphy端的port名 remote-endpoint = <&mipi_in_ucam0>; // csi2 dphy lane數(shù),1lane為 <1>, 4lane為 <1 2 3 4> // rk3568支持1*4lane和2*2lane兩種模式 data-lanes = <1 2 3 4>; }; }; }; };
然后需要配置CSI鏈路。RK3568的CSI有兩種工作模式,1*4LANE和2*2LANE。如果是前者,則需要使能csi2_dphy0,同時禁用csi2_dphy1/csi2_dphy2。后者相反。CSI的輸入端為GC8034,4LANE連接,輸出端為ISP,因此配置如下。
&csi2_dphy0 { //csi2_dphy0不與csi2_dphy1/csi2_dphy2同時使用,互斥 status = "okay"; ports { #address-cells = <1>; #size-cells = <0>; port@0 { reg = <0>; #address-cells = <1>; #size-cells = <0>; mipi_in_ucam0: endpoint@2 { reg = <2>; remote-endpoint = <&gc8034_out>; data-lanes = <1 2 3 4>; }; }; port@1 { reg = <1>; #address-cells = <1>; #size-cells = <0>; csidphy_out: endpoint@0 { reg = <0>; remote-endpoint = <&isp0_in>; }; }; }; };
然后ISP只需要配置輸入端,為CSI
&rkisp_vir0 { status = "okay"; port { #address-cells = <1>; #size-cells = <0>; isp0_in: endpoint@0 { reg = <0>; remote-endpoint = <&csidphy_out>; }; }; };
配置完這個鏈路之后,編譯內(nèi)核,然后將內(nèi)核燒錄到板上,即可使用攝像頭。
內(nèi)核日志如下
如果看到這個log,說明gc8034和csi2-dphy的鏈路已通。
GC8034驅(qū)動介紹
下面介紹一下GC8034的驅(qū)動。在kernel里面,由于有V4L2這個框架的存在,因此多數(shù)cmos image sensor的驅(qū)動的框架流程都差不多,只是在寄存器操作上存在差別。
首先是驅(qū)動注冊方面,GC8034支持通過I2C進行寄存器配置,而它的CSI接口只能用于raw image數(shù)據(jù)的發(fā)送,并不能進行控制。因此,它是一個I2C device。驅(qū)動的開始便是注冊了一個I2C device。如下
在設(shè)備樹上面的設(shè)備描述和驅(qū)動匹配時,便會執(zhí)行probe函數(shù)。下面看下probe函數(shù)。
static int gc8034_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; struct device_node *node = dev->of_node; struct gc8034 *gc8034; struct v4l2_subdev *sd; char facing[2]; int ret; ... gc8034 = devm_kzalloc(dev, sizeof(*gc8034), GFP_KERNEL); if (!gc8034) return -ENOMEM; // 這些數(shù)據(jù)是為了 sensor 的 ioctl 獲取模組狀態(tài) // RK平臺的一些上層應(yīng)用(比如android的camera應(yīng)用) 需要獲取這些信息,以便實現(xiàn)前后攝像頭切換,3A參數(shù)匹配等 ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX, &gc8034->module_index); ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING, &gc8034->module_facing); ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME, &gc8034->module_name); ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME, &gc8034->len_name); ... gc8034->client = client; gc8034->xvclk = devm_clk_get(dev, "xvclk"); ... // 電源 復(fù)位等 gpio 獲取 ... ret = gc8034_configure_regulators(gc8034); ... // 這個函數(shù)是獲取與之匹配的接口,也就是CSI的設(shè)備樹配置,主要是lane數(shù) ret = gc8034_parse_of(gc8034); ... // pinctrl資源獲取 ... mutex_init(&gc8034->mutex); // 最重要的函數(shù) v4l2_i2c_subdev_init 此函數(shù)注冊了一個v4l2 subdev,也就是將此攝像頭注冊到v4l2框架里面了 // 其中 gc8034_subdev_ops 里面的就是gc8034響應(yīng)上層控制的回調(diào)函數(shù) sd = &gc8034->subdev; v4l2_i2c_subdev_init(sd, client, &gc8034_subdev_ops); ret = gc8034_initialize_controls(gc8034); ... // 上電 ret = __gc8034_power_on(gc8034); ... // 識別一下i2c上面是不是有g(shù)c8034存在,不存在的話會將上面的操作全部注銷掉 ret = gc8034_check_sensor_id(gc8034, client); ... // 讀取gc8034 otp寄存器 gc8034_otp_enable(gc8034); gc8034_otp_read(gc8034); gc8034_otp_disable(gc8034); #ifdef CONFIG_VIDEO_V4L2_SUBDEV_API sd->internal_ops = &gc8034_internal_ops; sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE | V4L2_SUBDEV_FL_HAS_EVENTS; #endif #if defined(CONFIG_MEDIA_CONTROLLER) gc8034->pad.flags = MEDIA_PAD_FL_SOURCE; sd->entity.function = MEDIA_ENT_F_CAM_SENSOR; ret = media_entity_pads_init(&sd->entity, 1, &gc8034->pad); if (ret < 0) goto err_power_off; #endif ... // 此v4l2設(shè)備采用異步注冊 ret = v4l2_async_register_subdev_sensor_common(sd); ... return 0; ... }
整個probe函數(shù)很長,這里刪掉了那些錯誤處理和打印的部分,并為剩下的操作提供了注釋。它的操作流程就是先從設(shè)備樹上獲取信息,然后申請gpio等資源,注冊v4l2設(shè)備,然后嘗試讀取一下gc8034的id,如果gc8034存在,則讀取其otp寄存器。在kernel啟動的過程中,這些信息就是操作otp寄存器打印出來的
一般來說,模組出廠的時候要進行校準(zhǔn),因為各個廠家基于同一個sensor芯片設(shè)計的模組存在硬件上面的差異,校準(zhǔn)的內(nèi)容包括AF(自動對焦校準(zhǔn))、AWB(白平衡校準(zhǔn))、LSC(鏡頭陰影校準(zhǔn)),以及模組的出廠年月日廠商名等。前者一般需要被上層獲取,以便上層配置校準(zhǔn)參數(shù)。
然后就是v4l2框架里面幾個比較重要的結(jié)構(gòu)體。如下
static const struct v4l2_subdev_core_ops gc8034_core_ops = { .s_power = gc8034_s_power, .ioctl = gc8034_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl32 = gc8034_compat_ioctl32, #endif }; static const struct v4l2_subdev_video_ops gc8034_video_ops = { .s_stream = gc8034_s_stream, .g_frame_interval = gc8034_g_frame_interval, .g_mbus_config = gc8034_g_mbus_config, }; static const struct v4l2_subdev_pad_ops gc8034_pad_ops = { .enum_mbus_code = gc8034_enum_mbus_code, .enum_frame_size = gc8034_enum_frame_sizes, .enum_frame_interval = gc8034_enum_frame_interval, .get_fmt = gc8034_get_fmt, .set_fmt = gc8034_set_fmt, }; static const struct v4l2_subdev_ops gc8034_subdev_ops = { .core = &gc8034_core_ops, .video = &gc8034_video_ops, .pad = &gc8034_pad_ops, };
其中,
v4l2_subdev_core_ops主要是通用初始化的實現(xiàn),gc8034中實現(xiàn)了上下電操作,也就是gc8034_s_power函數(shù),還有一個ioctl操作,也就是gc8034_ioctl和gc8034_compat_ioctl32。
這個s_power函數(shù)主要借助Linux的電源管理框架(pm)來實現(xiàn)gc8034電源使能腳的拉高拉低,此驅(qū)動是實現(xiàn)了dev_pm_ops里面的suspend和resume函數(shù)的。
另外還有一個write_array函數(shù),這個函數(shù)主要用于初始化大多數(shù)的寄存器。大多數(shù)sensor的原廠會提供幾種分辨率下寄存器的配置表。將這個函數(shù)放在這里,意思就是每一次使能gc8034的電源之后都重新初始化所有寄存器。
而ioctl函數(shù)一般是實現(xiàn)平臺私有的一些ioctl命令(通用的v4l2 ioctl命令通過其它的ops實現(xiàn))。比如這里RKMODULE_GET_MODULE_INFO就是獲取設(shè)備樹配置的朝向和名稱等信息,RKMODULE_AWB_CFG就是獲取上文所述的otp寄存器里面的值。
v4l2_subdev_video_ops主要是對視頻流進行控制,其中s_stream是必須實現(xiàn)的,用于控制視頻流的開啟和關(guān)閉。g_frame_interval是用于獲取當(dāng)前幀率,g_mbus_config則是用于獲取lane數(shù)的,對于gc8034只能是2lane和4lane。
v4l2_subdev_pad_ops主要是對視頻格式進行控制。enum_mbus_codeenum_frame_size
enum_frame_interval三個成員是獲取當(dāng)前sensor支持的格式,分辨率以及幀率。get_fmt set_fmt兩個成員則是用于獲取和設(shè)置當(dāng)前sensor的格式分辨率和幀率。
對于rk平臺來說,要實現(xiàn)一個sensor驅(qū)動,實現(xiàn)上面的api就足夠了。如果是其它平臺,則要看是否需要私有的ioctl。通用的v4l2驅(qū)動是不需要私有ioctl的。
總結(jié)
本文以RK3568外接GC8034為例,首先介紹MIPI CSI攝像頭的適配方法,然后介紹cmos sensor驅(qū)動的一些細節(jié)與cmos sensor驅(qū)動的工作流程。參考GC8034的驅(qū)動,可以在通用平臺上實現(xiàn)sensor的適配。
審核編輯:劉清
-
寄存器
+關(guān)注
關(guān)注
31文章
5317瀏覽量
120015 -
MIPI
+關(guān)注
關(guān)注
11文章
308瀏覽量
48559 -
GPIO
+關(guān)注
關(guān)注
16文章
1196瀏覽量
51919 -
RK3568
+關(guān)注
關(guān)注
4文章
498瀏覽量
4935
原文標(biāo)題:RK3568 MIPI CSI攝像頭GC8034 適配
文章出處:【微信號:風(fēng)火輪技術(shù)團隊,微信公眾號:風(fēng)火輪技術(shù)團隊】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論