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;
}
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);
delay_us = ov13850_cal_delay(8192);
usleep_range(delay_us, delay_us * 2);
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";
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>;
dovdd-supply = <&vcc_mipi>;
dvdd-supply = <&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 出图效果预览:
返回总目录
|