1. 概述
前几章对摄像头V4L2
驱动开发各个环节做了展开分析,这一章节就做一个汇总,用来回顾前几章得知识点,概括一下摄像头开发得流程,本次使用RK3399
。
2. Sensor Sub-device 开发移植
Sensor
驱动位于 drivers/media/i2c
目录下,注意到本章节所描述的是具有 media controller
属性的 sensor
驱动, 故 drivers/media/i2c/soc_camera
目录下的驱动并不适用。
在 Media Controller
结构下, Sensor
一般作为 sub-device
并通过 pad
与 cif
、isp
或者 mipi phy
链接在一起。本章主要介绍 Sensor
驱动的代码[1
], dts
配置,及如何验证 sensor
驱动 的正确性。
本章将 Sensor
驱动的开发移植概括为 5
个部分
- 按照
datasheet
编写上电时序, 主要包括vdd
,reset
,powerdown
,clk
等。 - 配置
sensor
的寄存器以输出所需的分辨率、格式。 - 编写
struct v4l2_subdev_ops
所需要的回调函数,一般包括set_fmt
,get_fmt
,ov5695_s_stream
- 增加
v4l2 controller
用来设置如fps
,exposure
,gain
,test pattern
- 编写
.probe()
函数,并添加media control
及v4l2 sub device
初始化代码 作为良好的习惯,完成驱动编码后,也需要增加相应的Documentation
,可以参考Documentation/devicetree/bindings/media/i2c/
。这样板级dts
可以根据该文档快速配置。
在板级 dts
中,引用 Sensor
驱动,一般需要
- 配置正确的
clk
,io mux
- 根据原理图设置上电时序所需要的
regulator
及gpio
- 增加
port
子节点,与cif
或者isp
建立连接
本章以 ov13850
为例,简单分析 Sensor
驱动。
3. 上电时序
不同 Sensor
对上电时序要求不同,例如 OV Camera
。可能很大部分的 OV Camera
对时序 要求不严格,只要 mclk
,vdd
,reset
或 powerdown
状态是对的,就能正确进行 I2C
通讯并输出 图片。但还是有小部分 Sensor
对上电要求非常严格,例如 OV2685
必须严格按时序上电。 在 Sensor
厂家提供的 DataSheet
中,一般会有上电时序图,只需要按顺序配置即可。以 OV13850
为例,其中__ov13850_power_on()
即是用来给 Sensor
上电。如下(有删减)。
static int ov13850_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct device_node *node = dev->of_node;
struct ov13850 *ov13850;
struct v4l2_subdev *sd;
char facing[2];
int ret;
if (ov13850_master == ov13850) {
/* 获取时钟 */
ov13850->xvclk = devm_clk_get(dev, "xvclk");
if (IS_ERR(ov13850->xvclk)) {
dev_err(dev, "Failed to get xvclk\n");
return -EINVAL;
}
/* 获取复位gpio信息 */
ov13850->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(ov13850->reset_gpio))
dev_warn(dev, "Failed to get reset-gpios\n");
/* 配置电源 */
ret = ov13850_configure_regulators(dev);
if (ret) {
dev_err(dev, "Failed to get power regulators\n");
return ret;
}
/* 配置上电时序 */
ov13850->pwdn_gpio = devm_gpiod_get(dev, "pwdn", GPIOD_OUT_LOW);
if (IS_ERR(ov13850->pwdn_gpio))
dev_warn(dev, "Failed to get pwdn-gpios\n");
else
gpiod_set_value_cansleep(ov13850->pwdn_gpio, 0);
/* 上电 */
ret = __ov13850_power_on(ov13850);
return ret;
}
static int __ov13850_power_on(struct ov13850 *ov13850)
{
int ret;
u32 delay_us;
struct device *dev = &ov13850->client->dev;
struct i2c_client *client = ov13850->client;
unsigned short addr;
mutex_lock(&ov13850_power_mutex);
/* 上电 */
ret = __ov13850_master_power_on(dev);
if (ret) {
dev_err(dev, "could not power on, error %d\n", ret);
goto err_power;
}
/* 等待启动 */
usleep_range(500, 1000);
if (!IS_ERR(ov13850->pwdn_gpio))
gpiod_set_value_cansleep(ov13850->pwdn_gpio, 1);
/* 8192 cycles prior to first SCCB transaction */
delay_us = ov13850_cal_delay(8192);
usleep_range(delay_us, delay_us * 2);
/* Change i2c address by programming SCCB_ID */
addr = client->addr;
if (addr != OV13850_VENDOR_I2C_ADDR) {
client->addr = OV13850_VENDOR_I2C_ADDR;
ret = ov13850_write_reg(client, OV13850_REG_SCCB_ID,
OV13850_REG_VALUE_08BIT,
addr * 2);
if (ret) {
dev_err(dev, "write SCCB_ID failed\n");
goto err_i2c_addr;
}
client->addr = addr;
}
mutex_unlock(&ov13850_power_mutex);
return 0;
err_i2c_addr:
if (!IS_ERR(ov13850->pwdn_gpio))
gpiod_set_value_cansleep(ov13850->pwdn_gpio, 0);
__ov13850_master_power_off(dev);
err_power:
mutex_unlock(&ov13850_power_mutex);
return ret;
}
OV13850 的上电时序简要说明如下:
- 首先提供
xvclk
(即mclk
) - 紧接着
reset pin
使能 - 各路的
vdd
上电。这里使用了ov13850_configure_regulators
,里面调用了devm_regulator_bulk_get
,因为vdd
,vodd
,avdd
三者无严格顺序。如 果vdd
之间有严格的要求,需要分开处理,可参考OV2685
驱动代码 Vdd
上电后, 取消Sensor Reset
,powerdown
状态。Reset
,powerdown
可能只需要一 个,Sensor
封装不同, 可能有差异- 最后按时序要求,需要
8192
个clk cycle
之后,上电才算完成。
注意,虽然不按 datasheet
要求上电许多 Sensor
也能正常工作,但按原厂建议的时序操作, 无疑是最可靠的。
同样,datasheet
中还会有下电时序(Power Down Sequence
),也同样按要求即可。
3.1 判断上电时序是否正确
在.probe()
阶段会去尝试读取 chip id
,如 ov13850
的 ov13850_check_sensor_id()
,如果能够正确读取到 chip id
,一般就认为上电时序正确,Sensor
能够正常进行 i2c
通信。
ret = ov13850_check_sensor_id(ov13850, client);
4. Sensor 初始化寄存器列表
在 OV13850
及 OV13850
中,各定义了 struct ov13850_mode
及 struct ov13850_mode
, 用来表示 sensor
不同的初始化 mode
。Mode
可以包括如分辨率,mbus code
,寄存器初始化列 表等。
寄存器初始化列表,请按厂家提供的直接填入即可。需要注意的是, 列表最后用了 REG_NULL
表示结束。
static const struct regval ov13850_2112x1568_regs[] = {
{
0x3612, 0x27},
{
0x370a, 0x26},
{
0x372a, 0x00},
{
0x372f, 0x90},
{
0x3801, 0x08},
{
0x3805, 0x97},
{
0x3807, 0x4b},
{
0x3808, 0x08},
{
0x3809, 0x40},
{
0x380a, 0x06},
{
0x380b, 0x20},
{
0x380c, 0x12},
{
0x380d, 0xc0},
{
0x380e, 0x06},
{
0x380f, 0x80},
{
0x3813, 0x02},
{
0x3814, 0x31},
{
0x3815, 0x31},
{
0x3820, 0x02},
{
0x3821, 0x05},
{
0x3836, 0x08},
{
0x3837, 0x02},
{
0x4601, 0x04},
{
0x4603, 0x00},
{
0x4020, 0x00},
{
0x4021, 0xE4},
{
0x4022, 0x07},
{
0x4023, 0x5F},
{
0x4024, 0x08},
{
0x4025, 0x44},
{
0x4026, 0x08},
{
0x4027, 0x47},
{
0x4603, 0x01},
{
0x5401, 0x61},
{
0x5405, 0x40},
{
REG_NULL, 0x00},
};
static const struct ov13850_mode supported_modes[] = {
{
.width = 2112,
.height = 1568,
.max_fps = {
.numerator = 10000,
.denominator = 300000,
},
.exp_def = 0x0600,
.hts_def = 0x12c0,
.vts_def = 0x0680,
.reg_list = ov13850_2112x1568_regs,
},{
.width = 4224,
.height = 3136,
.max_fps = {
.numerator = 20000,
.denominator = 150000,
},
.exp_def = 0x0600,
.hts_def = 0x12c0,
.vts_def = 0x0d00,
.reg_list = ov13850_4224x3136_regs,
},
};
ov13850->cur_mode = &supported_modes[0];
5. V4l2_subdev_ops 回调函数
V4l2_subdev_ops
回调函数是 Sensor
驱动中逻辑控制的核心。回调函数包括非常多的功能, 具体可以查看 kernel
代码 include/media/v4l2-subdev.h
。建议 Sensor
驱动至少包括如下回调 函数。
.open
,这样上层才可以打开/dev/v4l-subdev
节点。在上层需要单独对sensor
设置v4l control
时,.open()
是必须实现的.s_stream
,即set stream
,包括stream on
和stream off
,一般在这里配置寄存器,使其 输出图像.enum_mbus_code
,枚举驱动支持的mbus_code
.enum_frame_size
,枚举驱动支持的分辨率.get_fmt
,返回当前Sensor
选中的format/size
。如果.get_fmt
缺失,media-ctl
工具无法 查看sensor entity
当前配置的fmt
.set_fmt
,设置Sensor
的format/size
以上回调中,.s_stream stream_on
会比较复杂些。在ov13850
驱动代码中,它包括pm_runtime
使能(即唤醒并上电),配置control
信息(v4l2 control
可能会在sensor
下电时 配置)即v4l2_ctrl_handler_setup()
,并最终写入寄存器stream on
。
以上回调中, .s_stream stream_on
会比较复杂些。在 ov13850
驱动代码中,它包括 pm_runtime
使能(即唤醒并上电),配置 control
信息(v4l2 control
可能会在 sensor
下电时 配置)即 v4l2_ctrl_handler_setup()
,并最终写入寄存器 stream on
。
6. V4l2 controller
对于需要配置 fps
,exposure
, gain
, blanking
的场景,v4l2 controller
部分是必要的。 OV13850
驱动代码中
ov13850_initialize_controls()
,用来声明支持哪些control
,并设置最大最小值等信息Struct v4l2_ctrl_ops
,包含了ov13850_set_ctrl()
回调函数,用以响应上层的设置。
7. Probe 函数及注册 media entity, v4l2 subdev
Probe
函数中, 首先对 dts
进行解析,获取 regulator
, gpio
, clk
等信息用以对 sensor
上下 电。其次注册 media entity
, v4l2 subdev
,及 v4l2 controller
信息。注意到 v4l2 subdev
的注 册是异步。如下几个关键的函数调用。
v4l2_i2c_subdev_init()
, 注册为一个v4l2 subdev
,参数中提供回调函数ov13850_initialize_controls()
,初始化v4l2 controls
media_entity_init()
,注册成为一个media entity
,OV13850
仅有一个输出,即Source Pad
v4l2_async_register_subdev()
,声明sensor
需要异步注册。因为RKISP1
及CIF
都 采用异步注册sub device
,所以这个调用是必须的。
8. dts 示例 subdev
Sensor 的 dts 配置大同小异,根据硬件的设计,主要是 pinctl(iomux),clk,gpio,以及 remote port。以下示例是在 RK3399-AIO 上的 OV13850 dts 节点。
ov13850: ov13850@36 {
compatible = "ovti,ov13850";
status = "okay";
reg = <0x36>;
clocks = <&cru SCLK_CIF_OUT>;
clock-names = "xvclk";
/* conflict with csi-ctl-gpios */
reset-gpios = <&gpio0 8 GPIO_ACTIVE_HIGH>;
pwdn-gpios = <&gpio2 0 GPIO_ACTIVE_HIGH>;
rockchip,camera-module-index = <0>;
rockchip,camera-module-facing = "back";
rockchip,camera-module-name = "CMK-CT0116";
rockchip,camera-module-lens-name = "Largan-50013A1";
avdd-supply = <&vcc_mipi>; /* VCC28_MIPI */
dovdd-supply = <&vcc_mipi>; /* VCC18_MIPI */
dvdd-supply = <&dvdd_1v2>; /* DVDD_1V2 */
port {
ucam_out0: endpoint {
remote-endpoint = <&mipi_in_ucam0>;
data-lanes = <1 2>;
};
};
};
Pinctrl
,声明了必要的pinctrl
, 该例子中包括了reset pin
初始化和clk iomux
Clock
,指定名称为xvclk
(驱动会讯取名为xvclk
的clock
),即24M
时钟Vdd supply
,OV13850
需要的三路供电Port
子节点,定义了一个endpoint
,声明需要与mipi_in_wcam
建立连接。同样地mipi dphy
会引用wcam_out
Data-lanes
指定了OV13850
使用两个lane
9. 使用gstreamer方式预览摄像头 subdev
gst-launch-1.0 v4l2src device=/dev/video0 ! video/x-raw,format=NV12,width=640,height=480, framerate=30/1 ! videoconvert ! kmssink &
ov13850
出图效果预览:
转载:https://blog.csdn.net/DSMGUOGUO/article/details/125778164