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 controlsmedia_entity_init(),注册成为一个media entity,OV13850仅有一个输出,即Source Padv4l2_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 iomuxClock,指定名称为xvclk(驱动会讯取名为xvclk的clock),即24M时钟Vdd supply,OV13850需要的三路供电Port子节点,定义了一个endpoint,声明需要与mipi_in_wcam建立连接。同样地mipi dphy会引用wcam_outData-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出图效果预览:

返回总目录










