Sensor 驱动调试指南
概述
编写目的
此文档旨在指导如何在 V85X、V821、V861 平台上进行 Sensor 驱动移植和调试,调试过程中常见问题的处理,以便于相关人员查看。
适用范围
适用于 Tina Linux 平台,Tina-v4.0 ,Tina-v5.0 V系列相关,V85x_TINA、V85xS_ARTMOS、V821_TINA、V861_TINA。
相关人员
Camera驱动维护人员或Sensor驱动开发人员。
术语与缩略词
MIPI:移动产业处理器接口联盟(Mobile Industry Processor Interface)
D-PHY:MIPI-CSI2 物理层(D-Core Physical Layer),将 Sensor 端发来的模拟电信号转换为数字信号
CSI2:MIPI-CSI2 协议层(Camera Serial Interface 2),将数字信号按照协议内容进行解析和检错
Lane:连接 TX 和 RX 端的一组差分线代表一组lane,如下图所示 1lane 与 2lane 硬件连接图


Mbps:Mbit per second,表示 MIPI 数据传输速度的单位,比如 OV4689 的 MIPI 速率是 672Mbps,一般指的是单条 lane 上每秒钟传输的数据量是672Mbit
DVP:Digital Video Port 是并口传输,数据位宽有 8bit、10bit、12bit 等,是非差分信号,最高速率要比串行传输的MIPI接口低
PCLK:pixel clock 像素时钟,每个时钟对应一个像素数据,一般为几十MHz
HSYNC:Horizonal Synchronization,是行同步信号。就是在告诉接收端,HSYNC有效时段内接收端接收到的所有的信号输出属同一行
VSYNC:Vertical Synchronization,是场同步信号。以高电平有效为例,VSYNC置高直到被拉低,这个区段所输出的所有影 像数据组成一个frame
MCLK:Master Clock,外部晶振或主控输出给Sensor的驱动时钟,典型值是24MHz、27MHz
SCL:Serial Clock Line,串行时钟线,负责产生同步时钟脉冲
SDA:Serial Data Line,串行数据线,负责在设备间传输串行数据
Camera 驱动概览
VIN 驱动目录
适用产品列表
:::
| 模块版本 | 驱动文件 |
|---|---|
| Linux-4.9 | drivers/media/platform/sunxi_vin/ |
| Linux-5.4/Linux-6.6 | bsp/drivers/vin/ |
V系列平台 sunxi-vin 驱动的目录概览:
sunxi-vin/
├── Kconfig
├── Makefile
├── modules
│ ├── sensor //sensor驱动文件目录
├── utility
├── vin.c //vin.c是驱动的主要功能实现,包括注册/注销、参数读取、与 v4l2 上层接口、与各 device 的下层接口、中断处理、buffer 申请切换等;
├── vin-cci
├── vin-csi //csi接口驱动文件
├── vin-isp
├── vin-mipi //mipi接口驱动文件
├── vin-stat
├── vin-tdm //TDM模块驱动文件
├── vin-video //video设备驱动文件
└── vin-vipp //VIPP模块驱动文件
Sensor 驱动目录
适用产品列表
:::
| 模块版本 | 驱动文件 |
|---|---|
| Linux-4.9 | drivers/media/platform/sunxi_vin/modules/sensor/ |
| Linux-5.4/Linux-6.6 | bsp/drivers/vin/modules/sensor/ |
V系列平台 Sensor 驱动的目录概览:
sensor/
├── ar0238.c
├── ar0238_hispi.c
├── bf2253l_mipi.c
├── bf2257cs_mipi.c
├── built-in.o
├── c2390a_mipi.c
├── C2398_mipi.c
├── c2399_mipi.c
├── camera_cfg.h
├── camera.h
├── f355p_dvp.c
├── f355p_mipi.c
├── f37h_mipi.c
├── f37p_mipi.c
├── f37p_mipi_vc.c
├── f53_mipi.c
├── gc02m1_mipi.c
├── gc0308_mipi.c
├── gc030a_mipi.c
├── gc0310_mipi.c
├── gc0339_mipi.c
├── gc0403_mipi.c
├── gc0406_mipi.c
....
Sensor 驱动数据结构
以 gc2053_mipi.c、gc1084_mipi.c 驱动文件为例,下面给出 Sensor 驱动关键数据结构的说明
// 【参考驱动文件目录】
// Linux-4.9
drivers/media/platform/sunxi_vin/modules/sensor/gc2053_mipi.c
// Linux-5.4或Linux-6.6
bsp/drivers/vin/modules/sensor/gc1084_mipi.c
Sensor 寄存器配置定义
struct regval_list 结构体用于填写 Sensor 初始化配置列表下需要写入的地址和值,配置由 Sensor 厂提供,struct regval_list 结构体定义如下,每一组分辨率和帧率下对应一个 regval_list 数组,regval_list 结构体变量的命名规则一般是 sensor_xxx(分辨率帧率)_regs。
struct regval_list {
addr_type addr;
data_type data;
};
/* 此结构体用于填写 Sensor 寄存器列表,由 Sensor原厂提供 */
static struct regval_list sensor_1080p12_regs[] = {
/* 1928*1088@12fps */
/****system****/
{0xfe, 0x80},
{0xfe, 0x80},
{0xfe, 0x80},
{0xfe, 0x00},
{0xf2, 0x00},
{0xf3, 0x00},
...
{0x02, 0x56},
{0x03, 0x8e},
{0x12, 0x80},
{0x13, 0x07},
{0x15, 0x12},
{0xfe, 0x00},
{0x17, 0x83},
};
Sensor 寄存器配置注册
/* Sensor 驱动由不同的分辨率/帧率的寄存器配置,它们填写在 sensor_win_sizes 结构体数组中 */
static struct sensor_win_size sensor_win_sizes[] = {
{
.width = 1928,
.height = 1088,
.hoffset = 4,//0,
.voffset = 4,//0,
.hts = 2200,
.vts = 2700,
.pclk = 74250000,
.mipi_bps = 297 * 1000 * 1000,
.fps_fixed = 12,//12.5
.bin_factor = 1,
.intg_min = 1 << 4,
.intg_max = (2700 - 16) << 4,
.gain_min = 1 << 4,
.gain_max = 110 << 4,
.regs = sensor_1080p12_regs,
.regs_size = ARRAY_SIZE(sensor_1080p12_regs),
.set_size = NULL,
},
...
};
| 成员变量 | 含义说明 |
|---|---|
| width | 图像输出宽度,定义 parser 接收进来的宽度 |
| height | 图像输出高度,定义 parser 接收进来的高度 |
| hoffset | 定义输入 ISP 的宽度偏移量,用于裁剪不需要的像素列 |
| voffset | 定义输入 ISP 的宽度偏移量,用于裁剪不需要的像素行 |
| hts | 行长(以pclk为单位) |
| vts | 帧长(以hts为单位) |
| pclk | 像素时钟:pclk = hts × vts × fps |
| mipi_bps | MIPI 速率:mipi bps = hts * vts * fps * raw bit(数据位宽) / lane num(mipi lane 数) |
| fps_fixed | 当前Sensor 寄存器配置对应的帧率 |
| bin_factor | ISP binning配置,默认配置为1 |
| intg_min | ISP可以设置的最小曝光时间(单位:曝光行),平台以16为一行,默认配置16 |
| intg_max | 最大曝光时间(默认为当前帧率的VTS,或者 VTS - offset(部分Sensor无法跑满理论VTS时需扣除一定offset,与FAE确认即可) |
| gain_min | ISP可以设置的Sensor最小增益,平台以16为一倍,默认配置16 |
| gain_max | ISP可以设置的Sensor最大增益 |
| regs | 当前帧率/分辨率对应的 Sensor 寄存器数组 |
| regs_size | 前帧率/分辨率对应的 Sensor 寄存器数组的长度 |
| set_size | 默认为NULL |
上述成员变量中,.hts 和.vts 值的设置必须与当前所使用的初始化寄存器配置互相对应,一般在 sensor 的 datasheet 中可以找到,如下,sensor 原厂提供的配置里面也会进行标注,如果没有找到可以询问一下 sensor 原厂。
下图是格科微 gc2053 datasheet 中找到配置 vts 寄存器值,对应格科微 gc2053 MIPI接口 2lane 1080p 12帧 的寄存器配置中{0x41, 0x0a},{0x42, 0x8c},所以对应的 vts 值为 0xa8c=2700,hts 值在 gc2053 datasheet 中没有找到,可以通过 pclk = hts × vts × fps 得出当前的 hts 值填写进去,或者询问 Sensor 原厂。

.intg_max 是最大曝光时间,示例中 gc2053 的最大曝光限制为 vts-16,因为主控端曝光是以 16 为一行,所以将 gc2053 最大曝光限制值左移 4 位填入.intg_max。每个 sensor 的最大曝光限制都是不一样的,这个可以询问一下 sensor 原厂或者翻阅 sensor datasheet 进行查找。
.gain_max 是最大增益,每个 sensor 的最大增益都是不一样的,这个可以询问一下 sensor 原厂或者翻阅 sensor datasheet 进行查找,本例中 sensor 的最大增益为 110 倍,而主控端 ISP 是以 16 为一倍,所以该值需要左移 4 位后填入。
.hoffset和.voffset 用于裁剪不需要的像素列和行,默认是居中裁剪,示例中hoffset为4,表示裁剪图像最左边4列和最右边4列,共裁剪8列,因此裁剪分辨率时,这个值填写为需要裁剪的列除以2,voffset同理。
如果 sensor 输出图像格式是 YUV 的话,则 .bin_factor/.intg_min/.intg_max/.gain_min/.gain_max 这几个成员变量无须填写。
Parser 裁剪宽高
sensor_win_sizes中的width和height用于配置soc parser接收的图像宽度和高度,所以通常也可以用于裁剪sensor图像的宽高(在不修改sensor寄存器配置的前提下)。
如图所示,假设sensor输出图像分辨率:1928x1088,但是应用需要的分辨率(居中裁剪):1920x1080, 按照如下配置,配置.hoffset为4,parser会裁剪掉左边4列,右边4列,即接收宽度为1920,同理,配置.voffset为的4,parser会裁剪掉上边4行,下边4列,接收高度为1080。
static struct sensor_win_size sensor_win_sizes[] = {
{
.width = 1928,
.height = 1088,
.hoffset = 4,//0,
.voffset = 4,//0,
...
.regs = sensor_1080p12_regs,
.regs_size = ARRAY_SIZE(sensor_1080p12_regs),
.set_size = NULL,
},
...
Sensor 图像数据格式注册
/*
* Here we'll try to encapsulate the changes for just the output
* video format.
*
*/
static struct regval_list sensor_fmt_raw[] = {
// 一般为空
};
/*
* Store information about the video data format.
*/
static struct sensor_format_struct sensor_formats[] = {
/* 定义 Sensor 输出的图像数据格式,根据Sensor输出格式填写,下述为 RAW 数据格式示例 */
{
.desc = "Raw RGB Bayer",
.mbus_code = MEDIA_BUS_FMT_SRGGB10_1X10,
.regs = sensor_fmt_raw,
.regs_size = ARRAY_SIZE(sensor_fmt_raw),
.bpp = 1
},
};
#define N_FMTS ARRAY_SIZE(sensor_formats)
// 如果Sensor 输出图像格式是 YUV,则需要根据 Sensor 图像数据输出顺序选择 YUYV/VYUY/UYVY/YVYU 其中一种,如下:
static struct sensor_format_struct sensor_formats[] = {
{
.desc = "YUYV 4:2:2",
.mbus_code = MEDIA_BUS_FMT_YUYV8_2X8,
.regs = sensor_fmt_raw,
.regs_size = ARRAY_SIZE(sensor_fmt_raw),
.bpp = 2,
},
};
#define N_FMTS ARRAY_SIZE(sensor_formats)
| 成员变量 | 含义说明 |
|---|---|
| desc | 描述 sensor 输出的图像格式 |
| mbus_code | 图像数据RGB分量排列顺序(常见 Bayer 格式:RGGB、BGGR、GRBG、GBRG) |
| regs | 默认填写sensor_fmt_raw |
| regs_size | 默认填写sensor_fmt_raw的大小 |
| bpp | 默认为1 |
Sensor I2C 注册
Sensor I2C 设备地址用于告知主控端丛机所对应的设备地址,以便主控端在通过 I2C 通讯时,能够根据从机(sensor)的设备地址对其进行读写寄存器操作,Sensor I2C 设备地址一般在 Sensor 的 datasheet 中可以找到,在 I2C 读写示例中有标明,如图所示,格科微 gc2053 datasheet 中描述了 Sensor I2C 设备地址。

需要注意的是,有些 sensor 支持通过修改外围电路设计从而更改设备 TWI 地址,在配置 TWI 设备地址时需要查看原理图或者询问硬件设计人员,如下是 gc2053 两组 TWI 地址:

驱动文件里面的 I2C_ADDR 宏定义一般用于填写具体的I2C地址
#define I2C_ADDR 0x6e /* sensor的TWI地址,I2C_ADDR要和board.dts中的sensor0_twi_addr一致*/
Sensor I2C 数据位宽和地址位宽是在 cci_driver 结构体中定义的,数据位宽和地址位宽必须按照手册说明进行配置,否则 I2C 通讯时没有将 sensor 寄存器值成功写入 sensor 中,导致 sensor 不出图。如下是 gc2053 datasheet I2C 通讯时序图。
/* 定义两组 Sensor CCi driver,CCi全称是camera control interface,由i2c和gpio组成 */
static struct cci_driver cci_drv[] = {
{
.name = SENSOR_NAME,
.addr_width = CCI_BITS_8,
.data_width = CCI_BITS_8,
}, {
.name = SENSOR_NAME_2,
.addr_width = CCI_BITS_8,
.data_width = CCI_BITS_8,
}
};
/* 用于下面Sensor_driver结构体来匹配设备树 */
static const struct i2c_device_id sensor_id[]
/* Sensor driver的定义,其中会通过id_table来匹配设备树 */
static struct i2c_driver sensor_driver[]

Sensor 电 气接口注册
Sensor 数据传输接口以及 Lane 数定义在 sensor_g_mbus_config 函数中进行填写。
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
__maybe_unused static int sensor_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
#if IS_ENABLED(CONFIG_SENSOR_GC2053_ONE_LANE_MIPI)
cfg->type = V4L2_MBUS_CSI2_DPHY;
cfg->bus.mipi_csi2.num_data_lanes = 0 | V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0;
#else /* two lane */
cfg->type = V4L2_MBUS_CSI2_DPHY;
cfg->bus.mipi_csi2.num_data_lanes = 0 | V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0;
#endif
return 0;
}
#else
__maybe_unused static int sensor_g_mbus_config(struct v4l2_subdev *sd,
struct v4l2_mbus_config *cfg)
{
#if IS_ENABLED(CONFIG_SENSOR_GC2053_ONE_LANE_MIPI)
cfg->type = V4L2_MBUS_CSI2_DPHY;
cfg->flags = 0 | V4L2_MBUS_CSI2_1_LANE | V4L2_MBUS_CSI2_CHANNEL_0;
#else /* two lane */
cfg->type = V4L2_MBUS_CSI2_DPHY;
cfg->flags = 0 | V4L2_MBUS_CSI2_2_LANE | V4L2_MBUS_CSI2_CHANNEL_0;
#endif
return 0;
}
#endif
Sensor 驱动接口概览
| 接口定义 | 接口说明 |
|---|---|
| sensor_g_fps | 获取 Sensor 实时帧率 |
| sensor_s_fps | 动态设置 Sensor 帧率 |
| sensor_g_exp | 获取 Sensor 曝光时间(单位:曝光行) |
| sensor_s_exp | 设置 Sensor 曝光时间(单位:曝光行) |
| sensor_g_gain | 获取 Sensor 当前增益 |
| sensor_s_gain | 设置 Sensor 增益 |
| sensor_s_exp_gain | 设置 Sensor 曝光时间和增益 |
| sensor_s_vflip | 动态设置 Sensor 垂直翻转 |
| sensor_s_hflip | 动态设置 Sensor 水平翻转 |
| sensor_g_flip | 获取 Sensor 当前翻转状态 |
| sensor_get_temp | 获取 Sensor 当前温度 |
| sensor_power | Sensor 上下电函数 |
| sensor_reset | 设置 Sensor 复位 |
| sensor_detect | Sensor 探测函数(测试IIC通信) |
| sensor_init | Sensor 驱动初始化入口 |
| sensor_ioctl | Sensor 功能函数系统调用入口 |
| sensor_g_mbus_config | Sensor 工作接口类型定义 |
| sensor_g_ctrl | Sensor v4l2_ctrl 功能函数 |
| sensor_s_ctrl | Sensor v4l2_ctrl 功能函数 |
| sensor_reg_init | Sensor 寄存器初始化函数 |
| sensor_s_stream | Sensor 开流函数 |
| sensor_init_controls | Sensor v4l2_ctrl 系统调用初始化函数 |
| sensor_probe | Sensor 驱动资源初始化函数 |
| sensor_remove | Sensor 驱动卸载函数 |
| init_sensor | Sensor 驱动注册函数 |
| exit_sensor | Sensor 驱动注销函数 |
Sensor 驱动点亮流程
线性模式
线性模式 Sensor 驱动移植根据 MIPI 接口和 DVP 接口来进行说明。
MIPI接口
获取 Sensor 初始化寄存器配置
MIPI接口线性模式Senso驱动r移植以格科微 gc2053 为例, 在调试Sensor驱动之前,需要确认以下几点:
| 图像规格 |
|---|
| Sensor使用哪类接口进行图像传输(MIPI、DVP) |
| 需要用到的分辨率和帧率 |
| MCLK频率(常见:24/27M) |
| 连接模组的外围功能电路是否按照sensor原厂提供硬件参考设计来实现的 |
根据当前方案所需要的分辨率和帧率,对应的MCLK(一般建议是24M)以及sensor硬件设计上所使用到接口(mipi/dvp)和lane数,联系sensor原厂提供一份对应的初始化寄存器配置,提供的配置需要和当前使用的模组匹配。如图所示是格科微 gc2053 MIPI接口 2lane 1080p 12帧 的寄存器配置:

添加驱动文件
1.添加Makefile文件
2.添加Sensor驱动文件
3.添加Kconfig
4.配置kernel_menuconfig
添加 MakeFile
进入 sensor 目录,打开 MakeFile 文件,添加指定语句,这一步的作用是将.c 源文件编译为.o 文件,如下图所示:

添加 Sensor 驱动文件
进入 sensor 目录,如果需要调试的模组对应的其他型号之前有在全志平台上点过的话,建议以 SDK 中的某个现成的驱动为基础修改,如格科微的 Sensor 驱动命名都是以 gc 开头的,思特威的 Sensor 驱动命名都是以 sc 开头的,索尼的 Sensor 驱动命名都是以 imx 开头的。 如果没有,可以找一份硬件配置(如 mipi lane 数,图像输出格式等)相近的驱动,在此基础上进行修改。本例中复用格科微其中一个 Sensor 的源文件,重命名成 gc2053_mipi.c。
添加 Kconfig