29 Linux MIPI摄像头驱动
在 Linux 中开发 MIPI 摄像头驱动,涉及到多方面的工作,包括设备树(DTS)配置、V4L2 子设备的注册、以及实现必要的 V4L2 接口。
这里我们来讨论一款MIPI接口的摄像头OV13855, OV13855 是一款 CMOS 图像传感器,使用 MIPI CSI-2 接口将数据传输给主机。因此,驱动开发主要围绕配置 MIPI 接口、传感器初始化以及与 V4L2 框架的集成。
29.1 DTS配置
设备树文件描述了硬件的连接和配置。OV13855 是一个 I2C 设备,因此我们需要在 DTS 中描述 I2C 接口和 MIPI CSI 接口。以下是设备树配置的关键部分:
/ {
vcc_mipidphy0: vcc-mipidphy0-regulator {
compatible = "regulator-fixed";
gpio = <&gpio1 RK_PB2 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <&mipidphy0_pwr>;
regulator-name = "vcc_mipidphy0";
enable-active-high;
//regulator-always-on;
//regulator-boot-on;
};
};
/****** common ******/
&rkcif {
status = "okay";
};
&rkcif_mmu {
status = "okay";
};
&i2c3 {
status = "okay";
ov13855_dph0: ov13855-dph0@10 {
compatible = "ovti,ov13855";
status = "okay";
reg = <0x10>;
clocks = <&cru CLK_MIPI_CAMARAOUT_M1>;
clock-names = "xvclk";
power-domains = <&power RK3588_PD_VI>;
pinctrl-names = "default";
pinctrl-0 = <&mipim0_camera1_clk >; //1b6
rockchip,grf = <&sys_grf>;
reset-gpios = <&gpio1 RK_PA3 GPIO_ACTIVE_HIGH>;
pwdn-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_HIGH>;
avdd-supply = <&vcc_mipidphy0>;
rockchip,camera-module-index = <1>;
rockchip,camera-module-facing = "front";
rockchip,camera-module-name = "CMK-OT2016-FV1";
rockchip,camera-module-lens-name = "default";
port {
ov13855_dphy0_out: endpoint {
remote-endpoint = <&mipi_in_dphy0_ov13855>;
data-lanes = <1 2 3 4>;
};
};
};
};
&csi2_dphy0_hw {
status = "okay";
};
&csi2_dphy0 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mipi_in_dphy0_ov13855: endpoint@1 {
reg = <1>;
remote-endpoint = <&ov13855_dphy0_out>;
data-lanes = <1 2 3 4>;
};
};
port@1 {
reg = <1>;
csidphy0_out: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi2_csi2_input>;
};
};
};
};
&mipi2_csi2 {
status = "okay";
ports {
#address-cells = <1>;
#size-cells = <0>;
port@0 {
reg = <0>;
mipi2_csi2_input: endpoint@1 {
reg = <1>;
remote-endpoint = <&csidphy0_out>;
};
};
port@1 {
reg = <1>;
mipi2_csi2_output: endpoint@0 {
reg = <0>;
remote-endpoint = <&cif_mipi_in2>;
};
};
};
};
&rkcif_mipi_lvds2 {
status = "okay";
port {
cif_mipi_in2: endpoint {
remote-endpoint = <&mipi2_csi2_output>;
};
};
};
&rkcif_mipi_lvds2_sditf {
status = "okay";
port {
mipi_lvds_sditf: endpoint {
remote-endpoint = <&isp0_vir0>;
};
};
};
&rkisp0 {
status = "okay";
};
&isp0_mmu {
status = "okay";
};
&rkisp0_vir0 {
status = "okay";
port {
#address-cells = <1>;
#size-cells = <0>;
isp0_vir0: endpoint@0 {
reg = <0>;
remote-endpoint = <&mipi_lvds_sditf>;
};
};
};
&pinctrl {
cam {
mipidphy0_pwr: mipidphy0-pwr {
rockchip,pins =
/* camera power en */
<1 RK_PB2 RK_FUNC_GPIO &pcfg_pull_none>;
};
pwdn_gpio: pwdn-gpio {
rockchip,pins =
/* camera power en */
<1 RK_PA2 RK_FUNC_GPIO &pcfg_pull_down_drv_level_15>;
};
};
};
上面DTS拓扑图如下:
+------------------+ +-------------+ +------------+ +------------------+ +--------+
| camera sensor | --> | csi2_dphy0 | --> | mipi2_csi2 | --> | rkcif_mipi_lvds2 | --> | rkisp |
+------------------+ +-------------+ +------------+ +------------------+ +--------+
其中camera sensor是设备厂商需要开发的, 其它的驱动Rockchip平台提供。
29.1.1 csi2_dphy0
功能: 该模块负责 MIPI D-PHY (Physical Layer) 的管理。它处理从传感器 (例如 ov13855 和 os04a10) 发送来的 MIPI 数据流,将这些信号转换为可以被 MIPI CSI-2 接收器处理的电信号。这是从摄像头传感器到处理器传输数据的物理层。
管理 D-PHY 通道的配置,包括数据通道的初始化、同步等。
处理数据的低层物理传输,将其传递给上层模块(比如 MIPI CSI-2 接口)。
驱动代码是: kernel/drivers/phy/rockchip/phy-rockchip-csi2-dphy.c
29.1.2 mipi2_csi2
功能: 该模块负责 MIPI CSI-2 协议层的数据接收。它接收从 D-PHY 层传递来的数据包(视频流),解包 CSI-2 帧并传递给系统中下游的处理器或图像信号处理器 (ISP)。此驱动类似与网络中的MAC层, 把DPHY物理信号转换成相对应格式视频数据。
负责 MIPI CSI-2 接口的初始化和配置,确保能够正确解包视频帧。
从 D-PHY 接收数据包,将其处理后传递给下游的视频处理单元或 ISP。
驱动代码是: kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c
29.2 上电时序
不同 Sensor 对上电时序要求不同,例如可能很大部分的 OV Sensor 对时序要求不严格,只要 mclk、vdd、reset 和 powerdown 状态是对的、就能正确进行 I2C 通讯并输出图片,而不用关心上电的先后顺序及延时。但还是有小部分 Sensor 对上电要求非常严格,例如 OV13855 必须严格按时序上电。
static int __ov13855_power_on(struct ov13855 *ov13855)
{
int ret;
u32 delay_us;
struct device *dev = &ov13855->client->dev;
if (!IS_ERR(ov13855->power_gpio))
gpiod_set_value_cansleep(ov13855->power_gpio, 1);
usleep_range(1000, 2000);
if (!IS_ERR_OR_NULL(ov13855->pins_default)) {
ret = pinctrl_select_state(ov13855->pinctrl,
ov13855->pins_default);
if (ret < 0)
dev_err(dev, "could not set pins\n");
}
ret = clk_set_rate(ov13855->xvclk, OV13855_XVCLK_FREQ);
if (ret < 0)
dev_warn(dev, "Failed to set xvclk rate (24MHz)\n");
if (clk_get_rate(ov13855->xvclk) != OV13855_XVCLK_FREQ)
dev_warn(dev, "xvclk mismatched, modes are based on 24MHz\n");
ret = clk_prepare_enable(ov13855->xvclk);
if (ret < 0) {
dev_err(dev, "Failed to enable xvclk\n");
return ret;
}
if (!IS_ERR(ov13855->reset_gpio))
gpiod_set_value_cansleep(ov13855->reset_gpio, 0);
ret = regulator_bulk_enable(OV13855_NUM_SUPPLIES, ov13855->supplies);
if (ret < 0) {
dev_err(dev, "Failed to enable regulators\n");
goto disable_clk;
}
if (!IS_ERR(ov13855->reset_gpio))
gpiod_set_value_cansleep(ov13855->reset_gpio, 1);
usleep_range(5000, 6000);
if (!IS_ERR(ov13855->pwdn_gpio))
gpiod_set_value_cansleep(ov13855->pwdn_gpio, 1);
/* 8192 cycles prior to first SCCB transaction */
delay_us = ov13855_cal_delay(8192);
usleep_range(delay_us * 2, delay_us * 3);
return 0;
disable_clk:
clk_disable_unprepare(ov13855->xvclk);
return ret;
}
在.probe()阶段会去尝试读取 chip id,如 ov13855 的 ov13855_check_sensor_id()
,如果能够正确读取到chip id,一般就认为上电时序正确,Sensor 能够正常进行 i2c 通信。
29.3 注册 V4L2 子设备
V4L2 框架是 Linux 内核中处理视频设备的核心模块。OV13855 作为 V4L2 的子设备,需要在驱动中注册并实现相应的接口。
static int ov13855_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
struct ov13855 *ov13855;
struct v4l2_subdev *sd;
char facing[2];
int ret;
dev_info(dev, "driver version: %02x.%02x.%02x",
DRIVER_VERSION >> 16,
(DRIVER_VERSION & 0xff00) >> 8,
DRIVER_VERSION & 0x00ff);
ov13855 = devm_kzalloc(dev, sizeof(*ov13855), GFP_KERNEL);
if (!ov13855)
return -ENOMEM;
ret = of_property_read_u32(node, RKMODULE_CAMERA_MODULE_INDEX,
&ov13855->module_index);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_FACING,
&ov13855->module_facing);
ret |= of_property_read_string(node, RKMODULE_CAMERA_MODULE_NAME,
&ov13855->module_name);
ret |= of_property_read_string(node, RKMODULE_CAMERA_LENS_NAME,
&ov13855->len_name);
if (ret) {
dev_err(dev, "could not get module information!\n");
return -EINVAL;
}
ov13855->client = client;
ov13855->cur_mode = &supported_modes[0];
ov13855->xvclk = devm_clk_get(dev, "xvclk");
if (IS_ERR(ov13855->xvclk)) {
dev_err(dev, "Failed to get xvclk\n");
return -EINVAL;
}
ov13855->power_gpio = devm_gpiod_get(dev, "power", GPIOD_OUT_LOW);
if (IS_ERR(ov13855->power_gpio))
dev_warn(dev, "Failed to get power-gpios, maybe no use\n");
ov13855->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ov13855->reset_gpio))
dev_warn(dev, "Failed to get reset-gpios\n");
ov13855->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
if (IS_ERR(ov13855->pwdn_gpio))
dev_warn(dev, "Failed to get pwdn-gpios\n");
ret = ov13855_configure_regulators(ov13855);
if (ret) {
dev_err(dev, "Failed to get power regulators\n");
return ret;
}
ov13855->pinctrl = devm_pinctrl_get(dev);
if (!IS_ERR(ov13855->pinctrl)) {
ov13855->pins_default =
pinctrl_lookup_state(ov13855->pinctrl,
OF_CAMERA_PINCTRL_STATE_DEFAULT);
if (IS_ERR(ov13855->pins_default))
dev_err(dev, "could not get default pinstate\n");
ov13855->pins_sleep =
pinctrl_lookup_state(ov13855->pinctrl,
OF_CAMERA_PINCTRL_STATE_SLEEP);
if (IS_ERR(ov13855->pins_sleep))
dev_err(dev, "could not get sleep pinstate\n");
}
mutex_init(&ov13855->mutex);
sd = &ov13855->subdev;
v4l2_i2c_subdev_init(sd, client, &ov13855_subdev_ops);
ret = ov13855_initialize_controls(ov13855);
if (ret)
goto err_destroy_mutex;
ret = __ov13855_power_on(ov13855);
if (ret)
goto err_free_handler;
usleep_range(25000, 30000);
ret = ov13855_check_sensor_id(ov13855, client);
if (ret)
goto err_power_off;
#ifdef CONFIG_VIDEO_V4L2_SUBDEV_API
sd->internal_ops = &ov13855_internal_ops;
sd->flags |= V4L2_SUBDEV_FL_HAS_DEVNODE;
#endif
#if defined(CONFIG_MEDIA_CONTROLLER)
ov13855->pad.flags = MEDIA_PAD_FL_SOURCE;
sd->entity.function = MEDIA_ENT_F_CAM_SENSOR;
ret = media_entity_pads_init(&sd->entity, 1, &ov13855->pad);
if (ret < 0)
goto err_power_off;
#endif
memset(facing, 0, sizeof(facing));
if (strcmp(ov13855->module_facing, "back") == 0)
facing[0] = 'b';
else
facing[0] = 'f';
snprintf(sd->name, sizeof(sd->name), "m%02d_%s_%s %s",
ov13855->module_index, facing,
OV13855_NAME, dev_name(sd->dev));
ret = v4l2_async_register_subdev_sensor_common(sd);
if (ret) {
dev_err(dev, "v4l2 async register subdev failed\n");
goto err_clean_entity;
}
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_idle(dev);
return 0;
err_clean_entity:
#if defined(CONFIG_MEDIA_CONTROLLER)
media_entity_cleanup(&sd->entity);
#endif
err_power_off:
__ov13855_power_off(ov13855);
err_free_handler:
v4l2_ctrl_handler_free(&ov13855->ctrl_handler);
err_destroy_mutex:
mutex_destroy(&ov13855->mutex);
return ret;
}
在.probe
函数里, 有调用v4l2_i2c_subdev_init
注册V4L2子设备, 接着就是初始I2C参数及读取camera id。
29.4 实现 V4L2 接口
实现V4L2必要的接口函数, 如下:
29.4.1 电源管理
static const struct dev_pm_ops ov13855_pm_ops = {
SET_RUNTIME_PM_OPS(ov13855_runtime_suspend,
ov13855_runtime_resume, NULL)
};
SET_RUNTIME_PM_OPS
:这个宏定义了运行时电源管理(runtime power management)相关的操作,包括:
29.4.2 v4l2_subdev_core_ops
static const struct v4l2_subdev_core_ops ov13855_core_ops = {
.s_power = ov13855_s_power,
.ioctl = ov13855_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl32 = ov13855_compat_ioctl32,
#endif
};
v4l2_subdev_core_ops
:定义核心操作,涵盖子设备的一些通用功能,如电源控制和自定义 I/O 操作。
ov13855_s_power
:用于控制摄像头传感器的电源。根据参数 on,可以开启或关闭摄像头电源(或复位)。
ov13855_ioctl
:用于处理应用程序通过 ioctl 系统调用发送的自定义命令。它可以扩展标准的 V4L2 控制功能,处理额外的摄像头配置操作。
ov13855_compat_ioctl32
:用于 32-bit 用户空间应用和 64-bit 内核之间的兼容处理,确保在 64-bit 系统上可以正常处理 32-bit 应用的 ioctl 请求。
29.4.3 v4l2_subdev_video_ops
static const struct v4l2_subdev_video_ops ov13855_video_ops = {
.s_stream = ov13855_s_stream,
.g_frame_interval = ov13855_g_frame_interval,
};
v4l2_subdev_video_ops
:定义视频相关的操作,主要用于处理摄像头传感器的视频流和帧控制。
29.4.4 v4l2_subdev_pad_ops
static const struct v4l2_subdev_pad_ops ov13855_pad_ops = {
.enum_mbus_code = ov13855_enum_mbus_code,
.enum_frame_size = ov13855_enum_frame_sizes,
.enum_frame_interval = ov13855_enum_frame_interval,
.get_fmt = ov13855_get_fmt,
.set_fmt = ov13855_set_fmt,
.get_selection = ov13855_get_selection,
.get_mbus_config = ov13855_g_mbus_config,
};
v4l2_subdev_pad_ops
:用于配置视频数据的传输格式、分辨率和相关参数的操作集。此类操作允许配置摄像头传感器的输出格式和尺寸。
ov13855_enum_mbus_code
:枚举支持的媒体总线格式(比如 Bayer 格式的 RAW 数据类型)。这有助于应用程序知道摄像头支持哪些数据格式。
ov13855_enum_frame_sizes
:枚举摄像头支持的帧尺寸(分辨率),比如 4208x3120。
ov13855_enum_frame_interval
:枚举支持的帧间隔(帧率)。
ov13855_get_fmt
:获取当前的帧格式(如分辨率和数据类型)。
ov13855_set_fmt
:设置帧格式,通常包括分辨率、数据类型等信息。
ov13855_get_selection
:获取当前的图像裁剪窗口或其他选择区域的参数。
ov13855_g_mbus_config
:获取媒体总线配置,比如 MIPI CSI-2 的通道配置、数据速率等。
29.4.5 v4l2_subdev_ops
static const struct v4l2_subdev_ops ov13855_subdev_ops = {
.core = &ov13855_core_ops,
.video = &ov13855_video_ops,
.pad = &ov13855_pad_ops,
};
v4l2_subdev_ops:整合所有不同操作集的接口定义。它将核心操作(core_ops)、视频流操作(video_ops)和数据格式操作(pad_ops)统一到一个子设备操作集,以便 V4L2 框架能够调用合适的接口函数。
29.5 数据格式
OV13855 输出的原始(RAW)数据格式通常是 Bayer 格式,这是图像传感器常用的 RAW 格式之一。具体来说,OV13855 支持 10-bit Bayer 格式,输出的 Bayer 数据格式通常为 BGGR,也就是在图像传感器上的颜色滤波阵列排列顺序是蓝(Blue)、绿(Green)、绿(Green)、红(Red)。
#define OV13855_LANES 4
#define OV13855_BITS_PER_SAMPLE 10
static const struct ov13855_mode supported_modes[] = {
{
.width = 4224,
.height = 3136,
.max_fps = {
.numerator = 10000,
.denominator = 300000,
},
.exp_def = 0x0800,
.hts_def = 0x0462,
.vts_def = 0x0c8e,
.bpp = 10,
.reg_list = ov13855_4224x3136_30fps_regs,
.link_freq_idx = 0,
},
#ifdef DEBUG
{
.width = 2112,
.height = 1568,
.max_fps = {
.numerator = 10000,
.denominator = 600000,
},
.exp_def = 0x0400,
.hts_def = 0x0462,
.vts_def = 0x0c89,
.bpp = 10,
.reg_list = ov13855_2112x1568_60fps_regs,
.link_freq_idx = 1,
},
{
.width = 4224,
.height = 3136,
.max_fps = {
.numerator = 10000,
.denominator = 150000,
},
.exp_def = 0x0800,
.hts_def = 0x08c4,
.vts_def = 0x0c8e,
.bpp = 10,
.reg_list = ov13855_4224x3136_15fps_regs,
.link_freq_idx = 0,
},
#endif
};
29.5.1 数据传输
(1) MIPI CSI 数据传输
OV13855 是一款使用 MIPI CSI-2 接口 的摄像头传感器。MIPI CSI(Camera Serial Interface)是一种高速串行数据传输接口,通常用于传输摄像头的原始图像数据。在数据传输过程中,OV13855 会将其捕获的图像数据(通常是 Bayer 格式的 RAW 数据)通过 MIPI 通道传输到主处理器mipi2_csi2
节点(Rockchip提供此V4L2子设备, 代码在kernel/drivers/media/platform/rockchip/cif/mipi-csi2.c)。
(2) ISP图像信号处理
虽然 OV13855 驱动本身负责数据的传输,但通常摄像头传感器输出的 Bayer RAW 数据需要通过 ISP 进行进一步处理。Rockchip ISP驱动代码在: kernel/drivers/media/platform/rockchip/isp/*
, ISP 的功能包括:
ISP 通常位于 SoC 中,并由平台的 ISP 驱动负责控制。OV13855 驱动与平台的 ISP 驱动之间通过 V4L2 框架进行数据和控制的交互。
Rockchip ISP驱动框图如下:
(3) 设备节点
在Neardi RK3588设备, 有如下视频节点:
neardi@LPA3588:~$ v4l2-ctl --list-devices
rk_hdmirx (fdee0000.hdmirx-controller):
/dev/video40
rkisp-statistics (platform: rkisp):
/dev/video29
/dev/video30
/dev/video38
/dev/video39
rkcif-mipi-lvds2 (platform:rkcif):
/dev/media0
/dev/media1
rkcif (platform:rkcif-mipi-lvds4):
/dev/video11
/dev/video12
/dev/video13
/dev/video14
/dev/video15
/dev/video16
/dev/video17
/dev/video18
/dev/video19
/dev/video20
/dev/video21
rkisp_mainpath (platform:rkisp0-vir0):
/dev/video22
/dev/video23
/dev/video24
/dev/video25
/dev/video26
/dev/video27
/dev/video28
/dev/media2
rkisp_mainpath (platform:rkisp1-vir1):
/dev/video31
/dev/video32
/dev/video33
/dev/video34
/dev/video35
/dev/video36
/dev/video37
/dev/media3
上面不同的视频节点, 视频数据处理算法不同。 在rkisp_mainpath里的节点, 是经过ISP算法之后的数据, 如下:
29.6 Debug驱动
(1). 安装V4L2工具v4l-utils
v4l-utils 包在 Ubuntu 系统下,可通过 apt 工具直接安装,如下,
sudo apt-get install v4l-utils
这样可以抓取不同视频节点的数据, 如下:
neardi@LPA3588:~$ v4l2-ctl -d /dev/video31 --set-fmt-video=width=1280,height=720,pixelformat=NV12 --stream-mmap=3 --stream-skip=3 --stream-to=./test.out --stream-count=1 --stream-poll
<<<<
neardi@LPA3588:~$ ls -lh test.out
-rw-rw-r-- 1 neardi neardi 1.4M Oct 12 07:07 test.out
(2). 使用media-ctl查看拓扑结构
media-ctl 是 v4l-utils 包中的一个工具,主要用来查看、配置 Media Framework 的各Entity的信息,如格
式、裁剪、链接使能等。应用该工具可以更灵活地拓展Camera功能。对于普通的应用场景,用户不必去配置具体的Entity信息,直接使用默认的就可以。
neardi@LPA3588:~$ media-ctl -p -d /dev/media1
Media controller API version 5.10.110
Media device information
------------------------
driver rkcif
model rkcif-mipi-lvds4
serial
bus info
hw revision 0x0
driver version 5.10.110
Device topology
- entity 1: stream_cif_mipi_id0 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video11
pad0: Sink
<- "rockchip-mipi-csi2":1 [ENABLED]
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 5: stream_cif_mipi_id1 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video12
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 [ENABLED]
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 9: stream_cif_mipi_id2 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video13
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 [ENABLED]
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 13: stream_cif_mipi_id3 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video14
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 [ENABLED]
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 17: rkcif_scale_ch0 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video15
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 [ENABLED]
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 21: rkcif_scale_ch1 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video16
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 [ENABLED]
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 25: rkcif_scale_ch2 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video17
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 [ENABLED]
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 29: rkcif_scale_ch3 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video18
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 [ENABLED]
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 33: rkcif_tools_id0 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video19
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 [ENABLED]
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 []
- entity 37: rkcif_tools_id1 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video20
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 [ENABLED]
<- "rockchip-mipi-csi2":11 []
- entity 41: rkcif_tools_id2 (1 pad, 11 links)
type Node subtype V4L flags 0
device node name /dev/video21
pad0: Sink
<- "rockchip-mipi-csi2":1 []
<- "rockchip-mipi-csi2":2 []
<- "rockchip-mipi-csi2":3 []
<- "rockchip-mipi-csi2":4 []
<- "rockchip-mipi-csi2":5 []
<- "rockchip-mipi-csi2":6 []
<- "rockchip-mipi-csi2":7 []
<- "rockchip-mipi-csi2":8 []
<- "rockchip-mipi-csi2":9 []
<- "rockchip-mipi-csi2":10 []
<- "rockchip-mipi-csi2":11 [ENABLED]
- entity 45: rockchip-mipi-csi2 (12 pads, 122 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev2
pad0: Sink
[fmt:SBGGR10_1X10/4224x3136 field:none
crop.bounds:(0,0)/4224x3136
crop:(0,0)/4224x3136]
<- "rockchip-csi2-dphy3":1 [ENABLED]
pad1: Source
-> "stream_cif_mipi_id0":0 [ENABLED]
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad2: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 [ENABLED]
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad3: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 [ENABLED]
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad4: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 [ENABLED]
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad5: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 [ENABLED]
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad6: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 [ENABLED]
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad7: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 [ENABLED]
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad8: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 [ENABLED]
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad9: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 [ENABLED]
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 []
pad10: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 [ENABLED]
-> "rkcif_tools_id2":0 []
pad11: Source
-> "stream_cif_mipi_id0":0 []
-> "stream_cif_mipi_id1":0 []
-> "stream_cif_mipi_id2":0 []
-> "stream_cif_mipi_id3":0 []
-> "rkcif_scale_ch0":0 []
-> "rkcif_scale_ch1":0 []
-> "rkcif_scale_ch2":0 []
-> "rkcif_scale_ch3":0 []
-> "rkcif_tools_id0":0 []
-> "rkcif_tools_id1":0 []
-> "rkcif_tools_id2":0 [ENABLED]
- entity 58: rockchip-csi2-dphy3 (2 pads, 2 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev3
pad0: Sink
[fmt:SBGGR10_1X10/4224x3136@10000/300000 field:none
crop.bounds:(0,0)/4224x3136]
<- "m01_f_ov13855 2-0010":0 [ENABLED]
pad1: Source
-> "rockchip-mipi-csi2":0 [ENABLED]
- entity 63: m01_f_ov13855 2-0010 (1 pad, 1 link)
type V4L2 subdev subtype Sensor flags 0
device node name /dev/v4l-subdev4
pad0: Source
[fmt:SBGGR10_1X10/4224x3136@10000/300000 field:none
crop.bounds:(0,0)/4224x3136]
-> "rockchip-csi2-dphy3":0 [ENABLED]
(3). 抓取Raw图
RKISP1的MP节点可以抓取Raw图,此时ISP是by-pass状态,不对数据做调制。
示例,抓取 Sensor OV13855 输出的 Raw Bayer 原始数据。格式为 SBGGR10_1X10/4224x3136 大小为 4224x3136。
从 media-ctl -p 的输出可以看到,OV13855 传感器连接到了 rockchip-csi2-dphy3,然后通过 CSI2 接口连接到 rockchip-mipi-csi2。
设置输出格式:
media-ctl -d /dev/media1 --set-v4l2 '"m01_f_ov13855 2-0010":0[fmt:SBGGR10_1X10/4224x3136]'
选择合适的视频节点
输出中显示,stream_cif_mipi_id0、stream_cif_mipi_id1、stream_cif_mipi_id2 和 stream_cif_mipi_id3 代表不同的 MIPI 通道(/dev/video11, /dev/video12, /dev/video13, /dev/video14),可以使用其中之一来抓取原始数据。以 stream_cif_mipi_id0(/dev/video11)为例,它连接到 CSI2 的第一个 pad(pad1),已经启用。
可以通过 v4l2-ctl 命令从 /dev/video11 设备节点抓取原始数据:
neardi@LPA3588:~$ v4l2-ctl --device /dev/video11 --stream-mmap --stream-count=100 --stream-o=output.raw
VIDIOC_REQBUFS returned -1 (Device or resource busy)
抓取失败, 使用/dev/video12:
neardi@LPA3588:~$ v4l2-ctl --device /dev/video11 --stream-mmap --stream-count=100 --stream-to=output.raw
VIDIOC_REQBUFS returned -1 (Device or resource busy)
neardi@LPA3588:~$ v4l2-ctl --device /dev/video12 --stream-mmap --stream-count=100 --stream-to=output.raw
<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 29.94 fps, dropped buffers: 2
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 29.94 fps
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< 29.95 fps
<<<<<<<<<<<
neardi@LPA3588:~$ ls output.raw -lh
-rw-rw-r-- 1 neardi neardi 1.6G Oct 12 07:59 output.raw
这个视频节点抓取成功。也可以把一帧RAW数据转换成一张图片, 如下:
neardi@LPA3588:~$ ffmpeg -f rawvideo -pix_fmt gray16le -s 4224x3136 -i output.raw -frames:v 1 -loglevel debug output_image.png
ffmpeg version 4.2.7-0ubuntu0.1 Copyright (c) 2000-2022 the FFmpeg developers
built with gcc 9 (Ubuntu 9.4.0-1ubuntu1~20.04.1)
configuration: --prefix=/usr --extra-version=0ubuntu0.1 --toolchain=hardened --libdir=/usr/lib/aarch64-linux-gnu --incdir=/usr/include/aarch64-linux-gnu --arch=arm64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx --enable-openal --enable-opencl --enable-opengl --enable-sdl2 --enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 31.100 / 56. 31.100
libavcodec 58. 54.100 / 58. 54.100
libavformat 58. 29.100 / 58. 29.100
libavdevice 58. 8.100 / 58. 8.100
libavfilter 7. 57.100 / 7. 57.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 5.100 / 5. 5.100
libswresample 3. 5.100 / 3. 5.100
libpostproc 55. 5.100 / 55. 5.100
Splitting the commandline.
Reading option '-f' ... matched as option 'f' (force format) with argument 'rawvideo'.
Reading option '-pix_fmt' ... matched as option 'pix_fmt' (set pixel format) with argument 'gray16le'.
Reading option '-s' ... matched as option 's' (set frame size (WxH or abbreviation)) with argument '4224x3136'.
Reading option '-i' ... matched as input url with argument 'output.raw'.
Reading option '-frames:v' ... matched as option 'frames' (set the number of frames to output) with argument '1'.
Reading option '-loglevel' ... matched as option 'loglevel' (set logging level) with argument 'debug'.
Reading option 'output_image.png' ... matched as output url.
Finished splitting the commandline.
Parsing a group of options: global .
Applying option loglevel (set logging level) with argument debug.
Successfully parsed a group of options.
Parsing a group of options: input url output.raw.
Applying option f (force format) with argument rawvideo.
Applying option pix_fmt (set pixel format) with argument gray16le.
Applying option s (set frame size (WxH or abbreviation)) with argument 4224x3136.
Successfully parsed a group of options.
Opening an input file: output.raw.
[rawvideo @ 0x55937f57b0] Opening 'output.raw' for reading
[file @ 0x55937c18f0] Setting default whitelist 'file,crypto'
[rawvideo @ 0x55937f57b0] Before avformat_find_stream_info() pos: 0 bytes read:32768 seeks:0 nb_streams:1
[rawvideo @ 0x55937f57b0] All info found
[rawvideo @ 0x55937f57b0] Estimating duration from bitrate, this may be inaccurate
[rawvideo @ 0x55937f57b0] After avformat_find_stream_info() pos: 26492928 bytes read:26492928 seeks:0 frames:1
Input #0, rawvideo, from 'output.raw':
Duration: 00:00:00.52, start: 0.000000, bitrate: 5187426 kb/s
Stream #0:0, 1, 1/25: Video: rawvideo, 1 reference frame (Y1[0][16] / 0x10003159), gray16le, 4224x3136, 0/1, 5298585 kb/s, 25 tbr, 25 tbn, 25 tbc
Successfully opened the file.
Parsing a group of options: output url output_image.png.
Applying option frames:v (set the number of frames to output) with argument 1.
Successfully parsed a group of options.
Opening an output file: output_image.png.
File 'output_image.png' already exists. Overwrite ? [y/N] y
Successfully opened the file.
Stream mapping:
Stream #0:0 -> #0:0 (rawvideo (native) -> png (native))
Press [q] to stop, [?] for help
cur_dts is invalid st:0 (0) [init:0 i_done:0 finish:0] (this is harmless if it occurs once at the start per stream)
[rawvideo @ 0x55937ff9e0] PACKET SIZE: 26492928, STRIDE: 8448
detected 8 logical cores
[graph 0 input from stream 0:0 @ 0x5593806f40] Setting 'video_size' to value '4224x3136'
[graph 0 input from stream 0:0 @ 0x5593806f40] Setting 'pix_fmt' to value '30'
[graph 0 input from stream 0:0 @ 0x5593806f40] Setting 'time_base' to value '1/25'
[graph 0 input from stream 0:0 @ 0x5593806f40] Setting 'pixel_aspect' to value '0/1'
[graph 0 input from stream 0:0 @ 0x5593806f40] Setting 'sws_param' to value 'flags=2'
[graph 0 input from stream 0:0 @ 0x5593806f40] Setting 'frame_rate' to value '25/1'
[graph 0 input from stream 0:0 @ 0x5593806f40] w:4224 h:3136 pixfmt:gray16le tb:1/25 fr:25/1 sar:0/1 sws_param:flags=2
[format @ 0x5593807350] Setting 'pix_fmts' to value 'rgb24|rgba|rgb48be|rgba64be|pal8|gray|ya8|gray16be|ya16be|monob'
[auto_scaler_0 @ 0x5593808940] Setting 'flags' to value 'bicubic'
[auto_scaler_0 @ 0x5593808940] w:iw h:ih flags:'bicubic' interl:0
[format @ 0x5593807350] auto-inserting filter 'auto_scaler_0' between the filter 'Parsed_null_0' and the filter 'format'
[AVFilterGraph @ 0x55937c1600] query_formats: 4 queried, 2 merged, 1 already done, 0 delayed
[auto_scaler_0 @ 0x5593808940] picking gray16be out of 10 ref:gray16le alpha:0
[auto_scaler_0 @ 0x5593808940] w:4224 h:3136 fmt:gray16le sar:0/1 -> w:4224 h:3136 fmt:gray16be sar:0/1 flags:0x4
Output #0, image2, to 'output_image.png':
Metadata:
encoder : Lavf58.29.100
Stream #0:0, 0, 1/25: Video: png, 1 reference frame, gray16be, 4224x3136, 0/1, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc
Metadata:
encoder : Lavc58.54.100 png
Clipping frame in rate conversion by 0.000008
No more output streams to write to, finishing.
[image2 @ 0x55937ffe10] Opening 'output_image.png' for writing
[file @ 0x5593ac26d0] Setting default whitelist 'file,crypto'
[AVIOContext @ 0x55937bed50] Statistics: 0 seeks, 83 writeouts
frame= 1 fps=0.5 q=-0.0 Lsize=N/A time=00:00:00.04 bitrate=N/A speed=0.0187x
video:21181kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown
Input file #0 (output.raw):
Input stream #0:0 (video): 1 packets read (26492928 bytes); 1 frames decoded;
Total: 1 packets (26492928 bytes) demuxed
Output file #0 (output_image.png):
Output stream #0:0 (video): 1 frames encoded; 1 packets muxed (21689255 bytes);
Total: 1 packets (21689255 bytes) muxed
1 frames successfully decoded, 0 decoding errors
[AVIOContext @ 0x55937bf320] Statistics: 26492928 bytes read, 0 seeks
(4). 打开调试开关
RKISP1或RKCIF驱动中都包含一些v4l2_dbg() log,通过命令可以打开 log 开关,如下。
root@LPA3588:~# echo 1 > /sys/module/video_rkcif/parameters/debug
root@LPA3588:~# ls /sys/module/video_rkcif/parameters/ -l
total 0
--w------- 1 root root 4096 Oct 12 08:13 clr_unready_dev
-rw-r--r-- 1 root root 4096 Oct 12 08:12 debug
-rw-r--r-- 1 root root 4096 Oct 12 08:13 debug_csi2
-r--r--r-- 1 root root 4096 Oct 12 08:13 version
root@LPA3588:~# cat /sys/module/video_rkcif/parameters/debug
1
打开 v4l2 相关的 log,比如 ioctl 调用。如下命令将 v4l2 相关 log 全部打开。
root@LPA3588:~# ls /sys/class/video4linux/video0/dev_debug
/sys/class/video4linux/video0/dev_debug
root@LPA3588:~# echo 1 > /sys/class/video4linux/video0/dev_debug
root@LPA3588:~# cat /sys/class/video4linux/video0/dev_debug
1
也可以分别只开一小部分的 log。如下Kernel宏定义了各个 bit 会 enable 哪些 log。将所需要的 log对应的bit 打开即可。这些宏定义在 kernel 头文件 include/media/v4l2-ioctl.h 中。
/* Just log the ioctl name + error code */
#define V4L2_DEV_DEBUG_IOCTL 0x01
/* Log the ioctl name arguments + error code */
#define V4L2_DEV_DEBUG_IOCTL_ARG 0x02
/* Log the file operations open, release, mmap and get_unmapped_area */
#define V4L2_DEV_DEBUG_FOP 0x04
/* Log the read and write file operations and the VIDIOC_(D)QBUF ioctls */
#define V4L2_DEV_DEBUG_STREAMING 0x08
/* Log poll() */
#define V4L2_DEV_DEBUG_POLL 0x10
29.7 总结
开发 Linux MIPI 摄像头驱动(如 OV13855)时,驱动的核心工作是配置摄像头传感器并通过 MIPI CSI 接口与主设备驱动协作。OV13855 驱动作为 V4L2 子设备,提供了丰富的接口供上层驱动和应用调用,包括电源管理、数据格式配置、视频流控制等。
主要步骤:
配置设备树,定义传感器的 GPIO、时钟和 MIPI 接口。
注册 V4L2 子设备并实现必要的操作接口。
实现 s_stream 和其他控制接口来控制摄像头的数据流。
通过 SoC 的 CSI 接口和 ISP 驱动完成数据的传输和处理。
通过这些步骤,驱动可以使摄像头传感器正常工作,图像数据能被正确传输、处理并提供给应用程序使用。