极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 3009|回复: 0

Linux(android?)系统 CSI 设备驱动开发

[复制链接]
发表于 2016-3-8 09:20:11 | 显示全部楼层 |阅读模式
CSI 简介
CSI 是采集 Cmos Sensor,Video Encoder 等视频输出设备信号的接口。该接
口支持 Hsync,Vsync 同步控制方式或内嵌同步 BT656 方式。一般 camera 模组
采用 Hsync,Vsync 同步方式。

CSI 硬件工作原理

CSI timing
Vref= positive; Href= positive
在 Vsync 和 Hsync 有效的区域内,通过 pclk 对 data 的采样,将图像数据 buffer 到 dram

CSI 硬件调试注意
1. 在初始化 sensor 前,请保证 sensor 各个电源的电压正确
2. 在初始化 sensor 前,请确保 reset,standby 按照 sensor 规定的上电时序控制,否则可能带来很多难以解释的问题
3. 往 sensor 写 I2C 命令前,请保证 MCLK 已经有信号输出,一般来说 MCLK 应该在 24MHz。
4. 如果初始化时,发现 I2C 写命令 fail,则应该检查 sensor 各个电源,power on 的时序,以及 MCLK 是否有信号。也可以尝试将 I2C 降速,或每写一个 I2C 命令后,延时一定时间。
5. 一般来说,初始化后,如果 PCLK 和 VSYNC,HSYNC 有信号输出,则初始化应该成功,如果 PCLK,VSYNC 和 HSYNC 的极性配置正确,则图像接受一般都正确。
6. 若发现接收到的图像有不规则出现的绿横线,一般来说,可能是 PCLK 的驱动能力不足。可以通过 I2C 调大 sensor 输出 PCLK 的驱动能力,一般可以解决问题。
7. 若发现接收到的图像有横向的细彩线出现,可能是 camera 的 avdd 受到干扰。可尝试加小电容滤波。
8. 若两个 sensor 共用到一个 CSI 上,出现切换时黑屏,花屏,有彩线等问题,原因往往出现在切换时,没有将对应的 sensor 的 IO PAD 切换到高阻状态,此时另外一个 sensor 的IO 输出时,就会被拉住。

Linux 系统 CSI 驱动程序
CSI 驱动文件目录结构
CSI 驱动的位置在 linux-3.0/drivers/media/video/sun4i_csi/
目录结构如下
|‐‐ sun4i_csi
........


CSI 驱动层次结构
CSI 驱动基于 Linux 的 V4L2 架构设计,满足标准的 V4L2 API 调用方式。由于 CSI 硬件采集的视频缓冲数据要求物理上连续的内存,故采用 video-dma-contig 这种连续的内存申请管理方式。除 V4L2 Kernel 相关的源代码外,CSI 驱动主体包括 CSI Host 和 Sensor ModuleV4L2 Subdev。对应的源码为 sun4i_csi_reg.c, sun4i_drv_csi.c 以及 camera 模组源代码(如gc0308.c,gt2005.c 等)。其中 sun4i_csi_reg.c 是 CSI 硬件的 HAL 层, sun4i_drv_csi.c 是 CSI 驱动的主体,实现 CSI 驱动的初始化,V4L2 API 对接以及 video buffer 的管理等。Camera 模组源代码(如 gc0308.c,gt2005.c 等),实现相应 camera 的初始化,分辨率,图像格式,白平衡,特效,曝光等效果设置,以及电源管理。

V4L2 Subdev 函数集
Camera 驱动开发者重点关注的应该为 camera 模组与 CSI 主体之间的接口函数。
其分为两大类别,分别为 sensor_core_ops 和 sensor_video_ops。sensor_core_ops定义 power,standby,效果设置以及 ioctl 扩展;sensor_video_ops 定义设置图像数据格式以及帧率的接口。其具体定义如下:

static const struct v4l2_subdev_core_ops sensor_core_ops = {
.g_chip_ident = sensor_g_chip_ident,
.g_ctrl = sensor_g_ctrl,
.s_ctrl = sensor_s_ctrl,
.queryctrl = sensor_queryctrl,
.reset = sensor_reset,
.init = sensor_init,
.s_power = sensor_power,
.ioctl = sensor_ioctl,
.......


sensor_reset 函数
Prototype:static int sensor_reset(struct v4l2_subdev *sd, u32 val)
Main Function:实现三个与 reset 相关的指令
switch(val)
{
case CSI_SUBDEV_RST_OFF:
//控制摄像头 IO 或发送 I2C 命令使其 release reset
break;
case CSI_SUBDEV_RST_ON:
//控制摄像头 IO 或发送 I2C 命令使其 hold reset
break;
case CSI_SUBDEV_RST_PUL:
//控制摄像头 IO 或发送 I2C 命令使其经过 reset releaseholdrelease 的时序
break;
default:
return -EINVAL;
}

sensor_power 函数
Prototype:static int sensor_power(struct v4l2_subdev *sd, int on)
Main Function: 实现 4 个与 power/standby 相关的指令
switch(on)
{
case CSI_SUBDEV_STBY_ON:
//控制 sensor 进入 standby 的时序
break;
case CSI_SUBDEV_STBY_OFF:
//控制 sensor 退出 standby 的时序
break;
case CSI_SUBDEV_PWR_ON:
//控制 sensor 上电的时序
break;
case CSI_SUBDEV_PWR_OFF:
//控制 sensor 关电的时序
break;
default:
return -EINVAL;
}


sensor_init 函数
Prototype:static int sensor_init(struct v4l2_subdev *sd, u32 val)
Main Function:实现检测 sensor id,以及对 sensor 初始化。其中,sensor_detect 函数实现读
取 sensor 的 id 值,若与目标相同则返回 ok;sensor_default_regs 则是保存 sensor 初始化 I2C
命令的数组,不同 sensor 需要填不同的 I2C 初始化参数。
ret = sensor_detect(sd);
if (ret) {
csi_dev_err("chip found is not an target chip.\n");
return ret;
}
return sensor_write_array(sd, sensor_default_regs , ARRAY_SIZE(sensor_default_regs));

sensor_queryctrl 函数
Prototype:static int sensor_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
Main Function:返回 sensor 支持的各种效果设置,以及对应的设置最大值/最小值/步长。
需要注意的是,若实际操作中,sensor 不支持的效果特性,最好在这里注释掉,以免上层进
行 query 时,误认为 sensor 支持。一般而言,除了 GAIN 参数外,其他各个设置的最大值/
最小值/步长都不能再做修改。基本需要实现的参数设置是 VFLIP,HFLIP(这两个参数涉
及到摄像头的成像方向,对应 sys_config1 文件配置中的 csi_hflip 与 csi_vflip)

EXPOSURE(曝光目标值)
,DO_WHITE_BALANCE(各种白平衡场景)
,COLORFX(各种特效)

switch (qc->id) {
case V4L2_CID_BRIGHTNESS:
return v4l2_ctrl_query_fill(qc, -4, 4, 1, 1);
case V4L2_CID_CONTRAST:
return v4l2_ctrl_query_fill(qc, -4, 4, 1, 1);
case V4L2_CID_SATURATION:
return v4l2_ctrl_query_fill(qc, -4, 4, 1, 1);
case V4L2_CID_HUE:
..........

sensor_s_ctrl 函数
Prototype:static int sensor_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
Main Function:各种 sensor 效果特性的设置。基本实现 VFLIP,HFLIP,EXPOSURE,
DO_WHITE_BALANCE,AUTO_WHITE_BALANCE,COLORFX,其他可不实现。
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return sensor_s_brightness(sd, ctrl->value);
case V4L2_CID_CONTRAST:
return sensor_s_contrast(sd, ctrl->value);
.......
case V4L2_CID_CAMERA_FLASH_MODE:
return sensor_s_flash_mode(sd,
(enum v4l2_flash_mode) ctrl->value);
}
return -EINVAL;
值得注意的是,sensor_s_ctrl 里面调用到的各个函数,针对不同的 sensor,需要不
同的实现。下面说一下各个函数需要实现的内容。
sensor_s_brightness(sd, ctrl->value)
sensor_s_contrast(sd, ctrl->value)
sensor_s_saturation(sd, ctrl->value)
sensor_s_exp(sd, ctrl->value)
//实现亮度/对比度/饱和度/曝光目标的调节(ctrl->value 最小值为-4,最大值为 4, step=1)。将
//抽象出来的-4~4 这 9 个等级的值,对应 sensor 具体的寄存器设置。
//brightness 对应实现 sensor_brightness_zero_regs[]等寄存器数组
//contrast 对应实现 sensor_contrast_zero_regs[]等寄存器数组
//saturation 对应实现 sensor_saturation_zero_regs[]等寄存器数组
//exp 对应实现 sensor_ev_zero_regs[]等寄存器数组
sensor_s_hue(sd, ctrl->value)
//调节 sensor 具体的寄存器,设置色调

sensor_s_vflip(sd, ctrl->value)
sensor_s_hflip(sd, ctrl->value)
sensor_s_autowb(sd, ctrl->value)
sensor_s_autoexp(sd,(enum v4l2_exposure_auto_type) ctrl->value)
//设置 sensor vflip(upsidedown)
,hflip(mirror)
,AWB(自动白平衡)
,AE(自动曝光)的
//enable 位
sensor_s_wb(sd,(enum v4l2_whiteblance) ctrl->value)
//设置各种白平衡场景
//对应实现
//sensor_wb_auto_regs[],sensor_wb_cloud_regs[],sensor_wb_daylight_regs[],
//sensor_wb_incandescence_regs[],sensor_wb_fluorescent_regs[],sensor_wb_tungsten_regs[]
//等寄存器数组
sensor_s_colorfx(sd,(enum v4l2_colorfx) ctrl->value);
//设置各种特效
//对应实现
// sensor_colorfx_none_regs[],sensor_colorfx_bw_regs[],sensor_colorfx_sepia_regs[],
// sensor_colorfx_negative_regs[],sensor_colorfx_emboss_regs[],sensor_colorfx_sketch_regs[]
// sensor_colorfx_sky_blue_regs[],sensor_colorfx_grass_green_regs[],
// sensor_colorfx_skin_whiten_regs[],sensor_colorfx_vivid_regs[]
//等寄存器数组
sensor_s_flash_mode(sd,(enum v4l2_flash_mode) ctrl->value)
//设置闪光灯模式


sensor_g_ctrl 函数
Prototype:static int sensor_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
Main Function:获取各种 sensor 效果特性的设置。基本实现 VFLIP,HFLIP,EXPOSURE,
DO_WHITE_BALANCE,AUTO_WHITE_BALANCE,COLORFX,其他可不实现。
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
return sensor_g_brightness(sd, &ctrl->value);
.....................
case V4L2_CID_CAMERA_FLASH_MODE:
return sensor_g_flash_mode(sd, &ctrl->value);
}
return -EINVAL;

sensor_ioctl 函数
Prototype: static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
Main Function: 与 CSI 主体文件 sun4i_drv_csi.c 的一个扩展接口。通过__csi_subdev_info_t
结构体,传递模组的 clock,vsync,hsync 的极性,mclk 的频率,还有 iocfg 属性(用作两个
sensor 接到同一个 CSI 时,对应的模组是 id 0 还是 id 1)
。这个接口不能作任何修改。
static long sensor_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
{
int ret=0;
switch(cmd){
case CSI_SUBDEV_CMD_GET_INFO:
{
struct sensor_info *info = to_state(sd);
__csi_subdev_info_t *ccm_info = arg;
ccm_info->mclk = info->ccm_info->mclk ;
ccm_info->vref= info->ccm_info->vref ;
..................
info->ccm_info->iocfg = ccm_info->iocfg ;
break;
}
default:
return -EINVAL;
}
return ret;
}

sensor_enum_fmt 函数
Prototype:static int sensor_enum_fmt(struct v4l2_subdev *sd, unsigned index,
enum v4l2_mbus_pixelcode *code)
Main Function:通过*code 返回 index 对应的图像格式。其中 sensor_formats[]数组保存了所
有支持的图像格式。

if (index >= N_FMTS)
return -EINVAL;
*code = sensor_formats[index].mbus_code;
return 0;

sensor_try_fmt 函数
Prototype:static int sensor_try_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
Main Function:通过 v4l2_mbus_framefmt 数据结构,上层设定好图像数据格式,size 大小,
通过调用 sensor_try_fmt,分别与 sensor_formats[]数组里面的格式作比较,获取到支持的图
像格式;与 sensor_win_sizes[]数组的 size 做比较,获取最接近的支持 size 大小。调用该接口
后 , sensor 的 设 置 不 会 作 任 何 改 变 。 该 接 口 内 容 是 通 用 接 口 , 针 对 不 同 的 sensor ,
sensor_try_fmt 不需要作任何改动,
只是实现 sensor_formats[]和 sensor_win_sizes[]里面.regs
对应的寄存器数组便可以。
static int sensor_try_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)
{
return sensor_try_fmt_internal(sd, fmt, NULL, NULL);
}
static int sensor_try_fmt_internal(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt,
struct sensor_format_struct **ret_fmt,
struct sensor_win_size **ret_wsize)
{
int index;
struct sensor_win_size *wsize;
for (index = 0; index < N_FMTS; index++)
f (sensor_formats[index].mbus_code == fmt->code)
break;
if (index >= N_FMTS) {
/* default to first format */
index = 0;
fmt->code = sensor_formats[0].mbus_code;
}
if (ret_fmt != NULL)
*ret_fmt = sensor_formats + index;
/*
* Fields: the sensor devices claim to be progressive.
*/
fmt->field = V4L2_FIELD_NONE;
/*
* Round requested image size down to the nearest
* we support, but not below the smallest.
*/
for (wsize = sensor_win_sizes; wsize < sensor_win_sizes + N_WIN_SIZES;
wsize++)
if (fmt->width >= wsize->width && fmt->height >= wsize->height)
break;
if (wsize >= sensor_win_sizes + N_WIN_SIZES)
wsize--;
/* Take the smallest one */
if (ret_wsize != NULL)
*ret_wsize = wsize;
/*
* Note the size we'll actually handle.
*/
fmt->width = wsize->width;
fmt->height = wsize->height;
return 0;
}


sensor_s_fmt 函数
Prototype:static int sensor_s_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt)
Main Function:通过 v4l2_mbus_framefmt 数据结构,上层设定好图像数据格式,size 大小,
通过调用 sensor_s_fmt,首先会调用一次 sensor_try_fmt_internal,分别与 sensor_formats[]数
组里面的格式作比较,获取到支持的图像格式;与 sensor_win_sizes[]数组的 size 做比较,获
取最接近的支持 size 大小,并将对应的指针返回。然后将 sensor 设置到获取到的图像格式
和 size 大小。该接口内容是通用接口,针对不同的 sensor,sensor_s_fmt 不需要作任何改
动,只是实现 sensor_formats[]和 sensor_win_sizes[]里面.regs 对应的寄存器数组便可以。
static int sensor_s_fmt(struct v4l2_subdev *sd,
struct v4l2_mbus_framefmt *fmt)
{
int ret;
struct sensor_format_struct *sensor_fmt;
struct sensor_win_size *wsize;
struct sensor_info *info = to_state(sd);
ret = sensor_try_fmt_internal(sd, fmt, &sensor_fmt, &wsize);
if (ret)
return ret;
sensor_write_array(sd, sensor_fmt->regs , sensor_fmt->regs_size);
ret = 0;
if (wsize->regs)
{
ret = sensor_write_array(sd, wsize->regs , wsize->regs_size);
if (ret < 0)
return ret;
}
if (wsize->set_size)
{
ret = wsize->set_size(sd);
if (ret < 0)
return ret;
}
info->fmt = sensor_fmt;
info->width = wsize->width;
info->height = wsize->height;
return 0;
}

CSI 驱动中 I2C 访问方式
sensor_write
Prototype:static int sensor_write(struct v4l2_subdev *sd, unsigned char *reg,
unsigned char *value)
以 gt2005.c 为例子,举例 16 位寄存器地址,8 位寄存器数据的 I2C 寄存器如何访问
往 0x0505 寄存器写 0xaa。
struct regval_list regs;
regs.reg_num[0] = 0x05;
regs.reg_num[1] = 0x05;
regs. value [0] = 0xaa;
ret = sensor_write(sd, regs.reg_num, regs.value);
if(ret < 0)
csi_dev_err("sensor_write err!\n");


sensor_read
Prototype:static int sensor_read(struct v4l2_subdev *sd, unsigned char *reg,
unsigned char *value)
以 gt2005.c 为例子,举例 16 位寄存器地址,8 位寄存器数据的 I2C 寄存器如何访问
读取 0x0505 寄存器的值,并打印出来。
struct regval_list regs;
regs.reg_num[0] = 0x05;
regs.reg_num[1] = 0x05;
ret = sensor_read(sd, regs.reg_num, regs.value);
if(ret < 0)
csi_dev_err("sensor_read err!\n");
else
csi_dev_print(“sensor read from 0x0505 = %x\n”,regs.value[0]);


sensor_write_array
Prototype:
static int sensor_write_array(struct v4l2_subdev *sd, struct regval_list *vals , uint size)
该函数实现对一个已定义的 struct regval_list 数组进行 I2C 写操作。
以 gt2005.c 为例子,在 sensor_init 里面,需要调用 sensor_default_regs 进行 I2C 写,从而对
sensor 初始化。

static struct regval_list sensor_default_regs[] = {
//......
}
sensor_write_array(sd, sensor_default_regs , ARRAY_SIZE(sensor_default_regs));





基于 SUN4I 平台的 Camera 模组移植
一般来说,增加对一个 camera 模组的支持,关键在于在 sun4i_csi/device/目录下,增加
xxx.c 文件,实现 poweron/off, standby on/off, reset 接口,设置好 mclk,vsync,hsync 的极性,
设置好 clock 的频率,最后在 sensor_default_regs[]以及 sensor_vga_regs[]填上初始化代码和
默认 VGA 分辨率的设置,应该就可以接收到基本的图像。

Camera 模组移植步骤
以现成的已经调试好的 gt2005.c 为例子,讲述一个新的 camera 模组应该如何移植。


Camera ID 修改
Camera ID 应该从原来的 gt2005 修改为一个特定容易辨认的 ID,而且需要在后续的
sys_config1.fex 里面的配置一致。
推荐将 Camera ID 修改为与 c 文件一样的名字,
以便辨认。
static const struct i2c_device_id sensor_id[] = {
{ "gt2005", 0 },
{}
};
static struct i2c_driver sensor_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "gt2005",
},
.probe = sensor_probe,
.remove = sensor_remove,
.id_table = sensor_id,
};


Camera 输出信号
各个模组输出的 MCLK,VSYNC,HSYNC 的极性有所不同,调试的时候可以根据 sensor
的初始化代码以及相应的 datasheet,或者通过 camera 模组代理商或 sensor 原厂,获取这些
信息。gt2005.c 中 ccm_info_con 这个结构体变量就是用来存储这些信息,通过 sensor_ioctl
扩展接口,与 sun4i_drv_csi.c 通信。
#define MCLK (24*1000*1000)
#define VREF_POL   CSI_HIGH
#define HREF_POL   CSI_HIGH
#define CLK_POL   CSI_RISING
#define IO_CFG   0
__csi_subdev_info_t ccm_info_con =
{
.mclk = MCLK,
.vref = VREF_POL,
.href = HREF_POL,
.clock = CLK_POL,
.iocfg = IO_CFG,
};
说明:
一般来说,通过设置 MCLK,VREF_POL,HREF_POL,CLK_POL 这几个宏定义就可以实
现。MCLK 的单位为 Hz,一般设置为 24MHz 或 12MHz 是准确的频率点,设置为其余的频
率是从系统的 PLL 获取,源头频率为 270MHz 或 297MHz,1~16 分频。VREF_POL,
HREF_POL 可以设置为 CSI_HIGH 或 CSI_LOW,意义为图像传输有效的区域,对应的
VSYNC 和 HSYNC 信号时高还是低。
CLK_POL 可以设置为 CSI_RISING 或 CSI_FALLING,
意义为 sensor 出来的 PCLK 是用上升沿还是下降沿采样数据。IO_CFG 是当两个 camera 共
用一个 CSI 时,标识对应 camera 的。一般是通过 sun4i_drv_csi.c 调用 sensor_ioctl 这个接口
来主动修改其值,用户不用关心。一般填写默认值 0 就可以。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|联系我们|极客工坊 ( 浙ICP备09023225号 )

GMT+8, 2019-4-23 20:38 , Processed in 0.044790 second(s), 24 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表