本文以RK3568外接GC8034為例,首先介紹MIPI CSI攝像頭的適配方法,然后介紹cmos sensor驅(qū)動的一些細節(jié)與cmos sensor驅(qū)動的工作流程。
硬件準備
首先介紹一下硬件。主板為風火輪科技的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ù)。
軟件準備
本文所有的源碼分析基于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寄存器打印出來的
一般來說,模組出廠的時候要進行校準,因為各個廠家基于同一個sensor芯片設(shè)計的模組存在硬件上面的差異,校準的內(nèi)容包括AF(自動對焦校準)、AWB(白平衡校準)、LSC(鏡頭陰影校準),以及模組的出廠年月日廠商名等。前者一般需要被上層獲取,以便上層配置校準參數(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是用于獲取當前幀率,g_mbus_config則是用于獲取lane數(shù)的,對于gc8034只能是2lane和4lane。
v4l2_subdev_pad_ops主要是對視頻格式進行控制。enum_mbus_codeenum_frame_size
enum_frame_interval三個成員是獲取當前sensor支持的格式,分辨率以及幀率。get_fmt set_fmt兩個成員則是用于獲取和設(shè)置當前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文章
5590瀏覽量
129165 -
MIPI
+關(guān)注
關(guān)注
11文章
346瀏覽量
50715 -
GPIO
+關(guān)注
關(guān)注
16文章
1313瀏覽量
55764 -
RK3568
+關(guān)注
關(guān)注
5文章
628瀏覽量
7596
原文標題:RK3568 MIPI CSI攝像頭GC8034 適配
文章出處:【微信號:風火輪技術(shù)團隊,微信公眾號:風火輪技術(shù)團隊】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
求助大神如何驅(qū)動TB8034攝像頭呢
RK3568單板的linux什么時候釋放支持雙路攝像頭的代碼
RK3568 CPU處理器測試步驟
RK3568 Sensor驅(qū)動包括了哪些回調(diào)函數(shù)
盤點RK3568為什么深受歡迎,且看RK3568核心板全新替代RK3288
RK3568 Ubuntu系統(tǒng)不能使用mipi攝像頭這是怎么回事
【飛凌RK3568開發(fā)板試用體驗】8-USB攝像頭實時AI物品識別初體驗
請問RK3568 MIPI RX DPHY接收MIPI協(xié)議的是否同時支持CSI與DSI兩種協(xié)議?
國產(chǎn)工業(yè)級RK3568核心板-AI人臉識別產(chǎn)品方案
基于Rockchip RK3568的路由器開發(fā)方案
STM32MP1序列產(chǎn)品連接MIPI?CSI-2攝像頭
RK3568麒麟系統(tǒng)板卡

RK3568 MIPI CSI攝像頭GC8034適配工作流程
評論