上章节讲到了 V4L2 control
的原理和实现,主要是设置图像处理和一些电源管理,一般不带ISP
摄像头设置完这些后就可以进行sbudev
的注册了,注册完基本就完成了整个驱动,具体分析如下。
drivers\media\i2c\ov13850.c
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;
}
一般摄像头都会有一个寄存器用来存放厂家ID
,用户可以用此ID
区分不同的摄像头,从而进行驱动分别适配,如ov13850厂家ID如下。
static int ov13850_check_sensor_id(struct ov13850 *ov13850,
struct i2c_client *client)
{
struct device *dev = &ov13850->client->dev;
u32 id = 0;
int ret;
ret = ov13850_read_reg(client, OV13850_REG_CHIP_ID,
OV13850_REG_VALUE_16BIT, &id);
if (id != CHIP_ID) {
dev_err(dev, "Unexpected sensor id(%06x), ret(%d)\n", id, ret);
return -ENODEV;
}
ret = ov13850_read_reg(client, OV13850_CHIP_REVISION_REG,
OV13850_REG_VALUE_08BIT, &id);
if (ret) {
dev_err(dev, "Read chip revision register error\n");
return ret;
}
if (id == OV13850_R2A)
ov13850_global_regs = ov13850_global_regs_r2a;
else
ov13850_global_regs = ov13850_global_regs_r1a;
dev_info(dev, "Detected OV%06x sensor, REVISION 0x%x\n", CHIP_ID, id);
return 0;
}
可以看到,摄像头注册的最后一步v4l2_async_register_subdev_sensor_common
就是将该设备注册进入v4l2
,然后与v4l2_device
进行匹配。
/* 注册subdev设备 */
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);
到这里OV13850
摄像头驱动基本完成,源码可以到第一章节下载查看,接下来就去测试摄像头出图。
在第一章节介绍摄像头的时候就已经介绍过media-ctl
这款v4l2
出图工具了,他是v4l-utils
的一个子工具,现在看看如何安装。
Ubuntu
安装方法sudo apt-get install v4l-utils
media-ctl --known-mbus-fmts
如下图,只是截取其中一部分格式,media-ctl
工具基本支持市面上所有常见的格式
驱动如果有支持 Media Controller
, 在 CIF
或 ISP
加载成功后会创建 media
设备, 如 /dev/media0
。利用 media-ctl
可以打印出当前的 pipeline
的链接情况。如下面的信息,就是rk3399
和ov13850
的链路拓扑结构。
root@firefly:/usr/local/bin# media-ctl -p /dev/media0
Media controller API version 0.1.0
Media device information
------------------------
driver rkisp1
model rkisp1
serial
bus info
hw revision 0x0
driver version 0.0.0
Device topology
- entity 1: rkisp1-isp-subdev (4 pads, 6 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev0
pad0: Sink
[fmt:SBGGR10_1X10/2112x1568 field:none
crop.bounds:(0,0)/2112x1568
crop:(0,0)/2112x1568]
<- "rkisp1_dmapath":0 []
<- "rockchip-mipi-dphy-rx":1 [ENABLED]
pad1: Sink
<- "rkisp1-input-params":0 [ENABLED]
pad2: Source
[fmt:YUYV8_2X8/2112x1568 field:none
crop.bounds:(0,0)/2112x1568
crop:(0,0)/2112x1568]
-> "rkisp1_selfpath":0 [ENABLED]
-> "rkisp1_mainpath":0 [ENABLED]
pad3: Source
-> "rkisp1-statistics":0 [ENABLED]
- entity 2: rkisp1_mainpath (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video0
pad0: Sink
<- "rkisp1-isp-subdev":2 [ENABLED]
- entity 3: rkisp1_selfpath (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video1
pad0: Sink
<- "rkisp1-isp-subdev":2 [ENABLED]
- entity 4: rkisp1_dmapath (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video2
pad0: Source
-> "rkisp1-isp-subdev":0 []
- entity 5: rkisp1-statistics (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video3
pad0: Sink
<- "rkisp1-isp-subdev":3 [ENABLED]
- entity 6: rkisp1-input-params (1 pad, 1 link)
type Node subtype V4L flags 0
device node name /dev/video4
pad0: Source
-> "rkisp1-isp-subdev":1 [ENABLED]
- entity 7: rockchip-mipi-dphy-rx (2 pads, 2 links)
type V4L2 subdev subtype Unknown flags 0
device node name /dev/v4l-subdev1
pad0: Sink
[fmt:SBGGR10_1X10/2112x1568@10000/300000 field:none]
<- "m00_b_ov13850 1-0036":0 [ENABLED]
pad1: Source
[fmt:SBGGR10_1X10/2112x1568@10000/300000 field:none]
-> "rkisp1-isp-subdev":0 [ENABLED]
- entity 8: m00_b_ov13850 1-0036 (1 pad, 1 link)
type V4L2 subdev subtype Sensor flags 0
device node name /dev/v4l-subdev2
pad0: Source
[fmt:SBGGR10_1X10/2112x1568@10000/300000 field:none]
-> "rockchip-mipi-dphy-rx":0 [ENABLED]
Entity
在 Media Controller
中,表示一个节点。它包含一个或多个的输入输出 pads
。Link
表示一条链接,它连接多个不同的 pad
。多个 Link
组成了一条完整的 pipeline
。
Entity
的名称可以从拓扑结构中查看,比如以下都是 entity
的名称:
ov2685 7-003c
rkisp1-isp-subdev
rkisp1_mainpath
Pad
由数字表示,一个 Entity
中可以包含多个 pad
,既可以有 Source
, 也可以有 Sink
。 Link
连接两个”entity
”:pad
,比如以下表示的链接,
rkisp1-isp-subdev
”:2
->“rkisp1_mainpath
”:0
rkisp1-isp-subdev
”:3
->“rkisp1-statistics
”:0
Active
, 也可以是 In-Active
。Media Controller
提供了灵活配置 pipeline
的功能, 在 CIF
、ISP
驱动初始化过程中,会根 据配置,将 Link
完整建立起来,如果有多个 Sensor
会激活其中的一个。 用户可以使用 media-ctl
命令修改 Link
的 Active
状态,也可以修改 Pad
的 format
、size
。
如图2
ISP
双 Camera
拓扑结构,有多个 Sensor
连接到同一个 Mipi D-Phy
, 同时只能有 一个是 Active
状态。以下示例设置 ov2659
为 Active
。
#首先将 ov5695 设置为 In-Active
media-ctl -l '"ov5695 7-0036":0->"rockchip-sy-mipi-dphy":0[0]'
#再将 ov2685 设置为 Active
media-ctl -l '"ov2685 7-003c":0->"rockchip-sy-mipi-dphy":0[1]'
注意:
media-ctl -l '"entity name":pad->"entity name":pad[Status]'
link
需要用单引号,因为有特殊字符,如 > [ ]
Entity name
需要用双引号,因为中间有空格Status
用 0
或 1
表示 Active
或 In-Active
,需要用中括号如图 2
ISP
双 Camera
拓扑结构,以下示例如何修改 fmt/size
。 示例一,修改 ov5695
输出的 size
为 640x480
(前提是 ov5695
驱动中已经有支持 640x480
输出,否则设置不会成功),并设置整个 pipeline
的输入输出分辨率都为 640x480
,同时 isp entity
的 format
为 YUYV
。
Sensor
输出 640x480
media-ctl -d /dev/media0 --set-v4l2 '"ov5695 7-0036":0[fmt:SBGGR10_1X10/640x480]'
media-ctl -d /dev/media0 --set-v4l2 '"rkisp1-isp-subdev":0[fmt:SBGGR10_1X10/640x480]'
media-ctl -d /dev/media0 --set-v4l2 '"rkisp1-isp-subdev":0[crop:(0,0)/640x480]'
media-ctl -d /dev/media0 --set-v4l2 '"rkisp1-isp-subdev":2[fmt:YUYV8_2X8/640x480]'
media-ctl -d /dev/media0 --set-v4l2 '"rkisp1-isp-subdev":2[crop:(0,0)/640x480]'
注意:
v4l-utils
其对应的 fmt
代码可能不同,最好用 media-ctl
查看 media-ctl --known-mbus-fmts
media-ctl --help
,查看更详细的使用帮助Media-ctl
工具的操作是通过/dev/medio0
等 media
设备,它所管理是 Media
的拓扑结构中 各个节点的 format
,大小,链接。V4l2-ctl
工具则是针对/dev/video0
,/dev/video1
等 video
设备,它在 video
设备上进行 set_fmt
,reqbuf
,qbuf
,dqbuf
,stream_on
,stream_off
等一 系列操作。
本文中主要用 v4l2-ctl
进行采集帧数据,设置曝光、gain
、VTS
等 v4l2_control
。
首先还是要建议先查看 v4l2-ctl
的帮助文档。帮助文档内容比较多,分成很多个部分,我们比 较关心,streaming
,vidcap
。
v4l2-ctl
帮助介绍v4l2-ctl --help
v4l2-ctl --help-all
streaming
相关的参数如下v4l2-ctl --help-streaming
vidcap
相关的参数如下。它主要包括 get-fmt
、set-fmt
等v4l2-ctl --help-vidcap
示例一,在 px3se-evb
板上,抓取 1
帧 nv12
数据保存到/tmp/cif.out
,分辨率为 720x480
; 在抓帧保存数据前,先丢弃前面 3
帧(即前面 3
帧虽然返回给 userspace
,但不保存到文件)。
v4l2-ctl -d /dev/video0 \
--set-fmt-video=width=720,height=480,pixelformat=NV12 \
--stream-mmap=3 \
--stream-skip=3 \
--stream-to=/tmp/cif.out \
--stream-count=1 \
--stream-poll
v4l2-ctl -d /dev/video0 --set-fmt-video=width=720,height=480,pixelformat=NV12 --stream-mmap=3 --stream-skip=3 --stream-to=/tmp/cif.out --stream-count=1 --stream-poll
参数的说明:
-d
,指定操作对象为/dev/video0
设备。
--set-fmt-video
,指定了宽高及 pxielformat
。
NV12
,即用 FourCC
表示的 pixelformat
。FourCC
编码详见上文 FourCC
。
--stream-mmap
,指定 buffer
的类型为 mmap
,即由 kernel
分配的物理连续的或经过iommu
映射的 buffer
。
--stream-skip
,指定丢弃(不保存到文件)前 3
帧
--stream-to
,指定帧数据保存的文件路径
--stream-count
,指定抓取的帧数, 不包括--stream-skip
丢弃的数量
--stream-poll
,该选项指示 v4l2-ctl
采用异步 IO
,即在 dqbuf
前先用 select
等等帧数据完 成,从而保证 dqbuf
不阻塞。否则 dqbuf
将会阻塞直到有数据帧到来。
如果 sensor
驱动有实现 v4l2 control
,在采集图像前, 可以通过 v4l2-ctl
设置如曝光、gain
等 v4l2 control
。 CIF
或 ISP
会继承 sub device 的 control
,因此这里通过/dev/video0
可以看到 Sensor
的 v4l2 control
。如下是 RK3399
机子上查看到的 OV13850
的相关设置,包括 exposure
,gain
,blanking
, test_pattern
等。
v4l2-ctl -d /dev/video0 -l
root@firefly:/home/firefly/driver_module# v4l2-ctl -d /dev/video0 -l
User Controls
exposure 0x00980911 (int) : min=4 max=1660 step=1 default=1536 value=1536
Image Source Controls
vertical_blanking 0x009e0901 (int) : min=96 max=31199 step=1 default=96 value=96
horizontal_blanking 0x009e0902 (int) : min=2688 max=2688 step=1 default=2688 value=2688 flags=read-only
analogue_gain 0x009e0903 (int) : min=16 max=248 step=1 default=16 value=16
Image Processing Controls
link_frequency 0x009f0901 (intmenu): min=0 max=0 default=0 value=0 flags=read-only
pixel_rate 0x009f0902 (int64) : min=0 max=120000000 step=1 default=120000000 value=120000000 flags=read-only
test_pattern 0x009f0903 (menu) : min=0 max=4 default=0 value=0
v4l2-ctl
可以修改这些 control
。如修改 exposure
及 analogue_gain
如下。v4l2-ctl -d /dev/video0 --set-ctrl 'exposure=1216,analogue_gain=10'
注意:
V4l2-ctl
存在 bug
,如多个 Camera
连到同一个 ISP
,只有查看、修改第一个 sensor
的 control
。至此,关于摄像头OV13850
驱动已经讲解完毕,后续还会深入v4l2
专题,这专题只需要大概了解摄像头的一些开发流程和框架,再后续可能会继续深入。
返回总目录