GC0308摄像头驱动程序使用的是linux v4l2协议,通过i2c信号进行控制。GC0308摄像头,对上电时序要求很严格,一定要根据datasheet初始化摄像头。
本驱动使用的3.10内核,所以首先要配置dts,在内核启动阶段支持摄像头,结合硬件原理图,首先配置硬件接口属性:
pinctrl_ipu1_4: ipu1grp-4 { /*++++ GC0308 camera */
fsl,pins = <
MX6QDL_PAD_GPIO_3__CCM_CLKO2 0x130b0 /*时钟*/
MX6QDL_PAD_CSI0_DAT10__GPIO5_IO28 0x80000000 /*CAM_nRST_CSI0_DAT10*/
MX6QDL_PAD_CSI0_DAT11__GPIO5_IO29 0x80000000 /* DAT11 */ /*CAM_SHDN_CSI0_DAT11*/
MX6QDL_PAD_CSI0_DAT12__IPU1_CSI0_DATA12 0x80000000 /*12-19八条数据线*/
MX6QDL_PAD_CSI0_DAT13__IPU1_CSI0_DATA13 0x80000000
MX6QDL_PAD_CSI0_DAT14__IPU1_CSI0_DATA14 0x80000000
MX6QDL_PAD_CSI0_DAT15__IPU1_CSI0_DATA15 0x80000000
MX6QDL_PAD_CSI0_DAT16__IPU1_CSI0_DATA16 0x80000000
MX6QDL_PAD_CSI0_DAT17__IPU1_CSI0_DATA17 0x80000000
MX6QDL_PAD_CSI0_DAT18__IPU1_CSI0_DATA18 0x80000000
MX6QDL_PAD_CSI0_DAT19__IPU1_CSI0_DATA19 0x80000000
MX6QDL_PAD_CSI0_DATA_EN__IPU1_CSI0_DATA_EN 0x80000000 /* 硬件悬空*/
MX6QDL_PAD_CSI0_PIXCLK__IPU1_CSI0_PIXCLK 0x80000000 /*像素时钟*/
MX6QDL_PAD_CSI0_MCLK__IPU1_CSI0_HSYNC 0x80000000
MX6QDL_PAD_CSI0_VSYNC__IPU1_CSI0_VSYNC 0x80000000
>;
};
&i2c3{
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c3_1>;
status = "okay";
gc0308: gc0308@21{
compatible = "gc0308-capture";
reg = <0x21>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_ipu1_4>; /* GC0308 camera*/
clocks = <&clks 201>;
clock-names = "csi_mclk";
DOVDD-supply = <&vgen4_reg>; /* 1.8v */
AVDD-supply = <&vgen3_reg>; /* 2.8v, on rev C board is VGEN3,
on rev B board is VGEN5 */
DVDD-supply = <&vgen2_reg>; /* 1.5v*/
pwn-gpios = <&gpio5 29 1>; /* active low: SD1_DAT0 */
rst-gpios = <&gpio5 28 0>; /* active high: SD1_DAT1 */
csi_id = <0>;
mclk = <24000000>;
mclk_source = <0>;
};
};
static const struct i2c_device_id gc0308_id[] = {
{"gc0308-capture", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, gc0308_id);
将gc0308_id加入i2c队列,当在dts中有相应名字的设备声明时,调用驱动程序的probe函数。
static struct i2c_driver gc0308_i2c_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "gc0308-capture",
},
.probe = gc0308_probe,
.remove = gc0308_remove,
.id_table = gc0308_id,
};
驱动程序初始化的时候,加入到i2c设备。
static s32 gc0308_write_reg(u8 reg, u8 val)
{
u8 au8Buf[3] = {0};
au8Buf[0] = reg;
au8Buf[1] = val;
if (i2c_master_send(gc0308_data.i2c_client, au8Buf, 2) < 0) {
pr_err("%s:write reg error:reg=%x,val=%x\n",
__func__, reg, val);
return -1;
}
return 0;
}
static s32 gc0308_read_reg(u8 reg, u8 *val)
{
u8 au8RegBuf[2] = {0};
u8 u8RdVal = 0;
au8RegBuf[0] = reg;
if (1 != i2c_master_send(gc0308_data.i2c_client, au8RegBuf, 1)) {
pr_err("%s:write reg error:reg=%x\n",
__func__, reg);
return -1;
}
if (1 != i2c_master_recv(gc0308_data.i2c_client, &u8RdVal, 1)) {
pr_err("%s:read reg error:reg=%x,val=%x\n",
__func__, reg, u8RdVal);
return -1;
}
return u8RdVal;
}
I2C数据读写
static struct v4l2_int_ioctl_desc gc0308_ioctl_desc[] = {
{vidioc_int_dev_init_num, (v4l2_int_ioctl_func *)ioctl_dev_init},
{vidioc_int_dev_exit_num, ioctl_dev_exit},
{vidioc_int_s_power_num, (v4l2_int_ioctl_func *)ioctl_s_power},
{vidioc_int_g_ifparm_num, (v4l2_int_ioctl_func *)ioctl_g_ifparm},
{vidioc_int_init_num, (v4l2_int_ioctl_func *)ioctl_init},
{vidioc_int_enum_fmt_cap_num,
(v4l2_int_ioctl_func *)ioctl_enum_fmt_cap},
{vidioc_int_g_fmt_cap_num, (v4l2_int_ioctl_func *)ioctl_g_fmt_cap},
{vidioc_int_g_parm_num, (v4l2_int_ioctl_func *)ioctl_g_parm},
{vidioc_int_s_parm_num, (v4l2_int_ioctl_func *)ioctl_s_parm},
{vidioc_int_g_ctrl_num, (v4l2_int_ioctl_func *)ioctl_g_ctrl},
{vidioc_int_s_ctrl_num, (v4l2_int_ioctl_func *)ioctl_s_ctrl},
{vidioc_int_enum_framesizes_num,
(v4l2_int_ioctl_func *)ioctl_enum_framesizes},
{vidioc_int_g_chip_ident_num,
(v4l2_int_ioctl_func *)ioctl_g_chip_ident},
};
一系列ioctl函数的指针数组,供应用程序调用。
static int get_device_id(void)
{
u8 au8RegBuf[2] = {0};
u8 u8RdVal = 0;
au8RegBuf[0] = 0x00;
if (1 != i2c_master_send(gc0308_data.i2c_client, au8RegBuf, 1)) {
pr_err("%s:write reg error:reg=%x\n",
__func__, 0xfb);
return -1;
}
if (1 != i2c_master_recv(gc0308_data.i2c_client, &u8RdVal, 1)) {
pr_err("%s:read reg error:reg=%x,val=%x\n",
__func__, 0xfb, u8RdVal);
return -1;
}
printk(KERN_INFO "u8RdVal=%x\n\n", u8RdVal);
return u8RdVal;
}
读取设备ID,具体读ID的指令,根据datasheet确定。
当ID成功读到了,接下来,设置多个摄像头属性,通常做成一个数组。此时摄像头基本能工作了。
当插入驱动模块以后会在/dev/ 目录下产生一个videoX 设备,这时候可以使用cheese xawtv等程序进行测试。