porting gc2035

本文介绍如何为GC2035摄像头模块编写Linux驱动程序。内容包括将驱动源码整合到内核中、配置GPIO接口、实现上电初始化序列及重置等功能。适用于希望了解摄像头驱动开发流程的技术人员。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文针对于linux3.0.8的内核 其他内核也差不多。


第一从fae那里拿到驱动源码,放到/kernel/drivers/media/video下。然后再Kconfig和Makefile添加相应的代码。
2.在kernel/include/media/下添加gc2035_platform.h头文件,可以仿照里面有现成的写。
3.在/kernel/arch/arm/mach-s5pv210/mach-mini210.c添加如下代码:
#ifdef CONFIG_VIDEO_GC2035


#define GC2035_PWR_EN S5PV210_MP05(1)
#define GC2035_RESET S5PV210_GPE1(4)
#define GC2035_PWR_DOWN_B S5PV210_MP05(0)
#define GC2035_FLED_EN S5PV210_MP05(2)
#define GC2035_FLED_MODE S5PV210_MP05(3)
#define GC2035_CAM_AF S5PV210_GPC0(4)
#define GC2035_PWR_DOWN S5PV210_MP04(7)




static void gc2035_pwr_first(void)
{
int err;


unsigned int tempvalue=0;




printk("%s\n",__func__);





err = gpio_request(GC2035_PWR_DOWN, "MP04_7");
if (err)
printk(KERN_ERR "#### failed to get GC2035_PWR_DOWN(MP04_7) for gc2035 CAM\n");


err = gpio_request(GC2035_PWR_DOWN_B, "MP05_0");
if (err)
printk(KERN_ERR "#### failed to get GC2035_PWR_DOWN_B(MP05_0) for gc2035 CAM\n");


err = gpio_request(GC2035_PWR_EN, "MP05_1");
if (err)
printk(KERN_ERR "#### failed to get GC2035_PWR_EN(MP05_1) for gc2035 CAM\n");


err = gpio_request(GC2035_RESET, "GPE1_4");
if (err)
printk(KERN_ERR "#### failed to get GC2035_RESET(GPE1_4) for gc2035 CAM\n");





gpio_direction_output(GC2035_PWR_DOWN_B, 1);

gpio_direction_output(GC2035_PWR_DOWN, 0);



gpio_direction_output(GC2035_RESET, 0);


mdelay(1);


s3c_gpio_setpull(GC2035_PWR_EN, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_PWR_EN, 1);
mdelay(100);




gpio_direction_output(GC2035_RESET, 1);



gpio_free(GC2035_PWR_DOWN);
gpio_free(GC2035_PWR_DOWN_B);
gpio_free(GC2035_PWR_EN);
gpio_free(GC2035_RESET);


mdelay(100);
}


static void gc2035_pwr_en(int enabled)
{
int err;


unsigned int tempvalue=0;


printk("%s\n",__func__);









err = gpio_request(GC2035_PWR_EN, "MP05_1");
if (err)
printk(KERN_ERR "#### failed to get(GPG1) for gc2035 CAM\n");


s3c_gpio_setpull(GC2035_PWR_EN, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_PWR_EN, enabled);
gpio_free(GC2035_PWR_EN);


mdelay(100);
}


static void gc2035_reset(void)
{
int err;


printk("%s\n",__func__);




err = gpio_request(GC2035_RESET, "GPE1");
if (err)
printk(KERN_ERR "#### failed to get(GPE1) for gc2035 CAM\n");


s3c_gpio_setpull(GC2035_RESET, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_RESET, 1);
mdelay(5);
gpio_direction_output(GC2035_RESET, 0);
mdelay(10);
gpio_direction_output(GC2035_RESET, 1);
gpio_free(GC2035_RESET);
}


static void gc2035_pwr_down(int enabled)
{
int err;

printk("%s\n",__func__);


err = gpio_request(GC2035_PWR_DOWN, "GPG1");
if (err)
printk(KERN_ERR "#### failed to reset(GPG1) for gc2035 CAM\n");


s3c_gpio_setpull(GC2035_PWR_DOWN, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_PWR_DOWN, enabled);
gpio_free(GC2035_PWR_DOWN);
}


static void gc2035_fled_en(int enabled)
{
int err;

printk("%s\n",__func__);


err = gpio_request(GC2035_FLED_EN, "GPG1");
if (err)
printk(KERN_ERR "#### failed to get(GPG1) for gc2035 CAM\n");


s3c_gpio_setpull(GC2035_FLED_EN, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_FLED_EN, enabled);
gpio_free(GC2035_FLED_EN);
}


static void gc2035_fled_mode(int mode)
{
int err;

printk("%s\n",__func__);


err = gpio_request(GC2035_FLED_MODE, "GPG1");
if (err)
printk(KERN_ERR "#### failed to get(GPG1) for gc2035 CAM\n");


s3c_gpio_setpull(GC2035_FLED_MODE, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_FLED_MODE, mode);
gpio_free(GC2035_FLED_MODE);
}


static void gc2035_cam_af(int enabled)
{
int err;

printk("%s\n",__func__);


err = gpio_request(GC2035_CAM_AF, "GPC0");
if (err)
printk(KERN_ERR "#### failed to get(GPC0) for gc2035 CAM\n");


s3c_gpio_setpull(GC2035_CAM_AF, S3C_GPIO_PULL_NONE);
gpio_direction_output(GC2035_CAM_AF, enabled);
gpio_free(GC2035_CAM_AF);
}


static int gc2035_pwr(int enabled)
{


printk("%s\n",__func__);


gc2035_reset();
gc2035_pwr_down(1);
gc2035_pwr_en(1);
gc2035_pwr_down(0);
gc2035_fled_en(0);
gc2035_cam_af(1);


return 0;
}


static struct gc2035_platform_data gc2035_plat = {
.pwr_en = gc2035_pwr_en,
.reset = gc2035_reset,
.pwr_down = gc2035_pwr_down,
.fled_en = gc2035_fled_en,
.fled_mode = gc2035_fled_mode,
.cam_af = gc2035_cam_af,


.default_width = 640,
.default_height = 480,
.pixelformat = V4L2_PIX_FMT_UYVY,
.freq = 24000000,
.is_mipi = 0,
};


static struct i2c_board_info  gc2035_i2c_info = {
I2C_BOARD_INFO("gc2035", GC2035_I2C_ADDR),
.platform_data = &gc2035_plat,
};


static struct s3c_platform_camera gc2035 = {
#ifdef CAM_ITU_CH_A
.id = CAMERA_PAR_A,
#else
.id = CAMERA_PAR_B,
#endif
.type = CAM_TYPE_ITU,
.fmt = ITU_601_YCBCR422_8BIT,
.order422 = CAM_ORDER422_8BIT_YCBYCR,
.i2c_busnum = 1,
.info = &gc2035_i2c_info,
.pixelformat = V4L2_PIX_FMT_UYVY,
//.srclk_name = "mout_mpll",
.srclk_name = "xusbxti",
.clk_name = "sclk_cam0",
.clk_rate = 24000000,
.line_length = 1920,
.width = 640,
.height = 480,
.window = {
.left = 0,
.top = 0,
.width = 640,
.height = 480,
},


/* Polarity */
.inv_pclk = 0,
.inv_vsync = 0,
.inv_href = 0,
.inv_hsync = 0,


.initialized = 0,
.cam_power = gc2035_pwr,
};
#endif




};






/* Interface setting */
static struct s3c_platform_fimc fimc_plat_lsi = {
.srclk_name = "mout_mpll",
.clk_name = "sclk_fimc",
.lclk_name = "sclk_fimc_lclk",
.clk_rate = 166750000,
#if defined(CONFIG_VIDEO_S5K4EA)
.default_cam = CAMERA_CSI_C,
#else
#ifdef CAM_ITU_CH_A
.default_cam = CAMERA_PAR_A,
#else
.default_cam = CAMERA_PAR_B,
#endif
#endif
.camera = {


#ifdef CONFIG_VIDEO_GC2035
&gc2035,
#endif


},
.hw_ver = 0x43,
};


这里是上电时序和添加i2c从设备地址。
调试时用示波器检测信号。
一般会碰到收不到sensorID,这就是i2c没信号。那就是i2c管脚不对。重新检测电路,用万用表看是否通路。
20161114全志R16配置为前ov5640后UVC的双摄像头 1、 Z:\home\wwt\uvc_r16_project\android\device\softwinner\astar-evb20\configs\camera.cfg 修改: number_of_camera = 1 为: number_of_camera = 2 如果不改这里,兄弟,你在camera这个APP里面是绝对看不到前后摄像头的切换选项的!!!!^_ 修改ov5640的分辨率: used_preview_size = 1 key_support_preview_size = 640x480 key_default_preview_size = 640x480 used_picture_size = 1 key_support_picture_size = 640x480 key_default_picture_size = 640x480 为(根据你驱动里面的摄像头的寄存器配置分辨率而实际修改): used_preview_size = 1 key_support_preview_size = 2592x1936, 2048x1536, 1600x1200, 1280x960, 1280x960, 1024x768, 1920x1080 ,1280x720, 800x600, 640x480 key_default_preview_size = 640x480 used_picture_size = 1 key_support_picture_size = 2592x1936, 2048x1536, 1600x1200, 1280x960, 1280x960, 1024x768, 1920x1080 ,1280x720, 800x600, 640x480 key_default_picture_size = 640x480 2、 Z:\home\wwt\uvc_r16_project\android\device\softwinner\astar-evb20\astar_evb20.mk #include device/softwinner/polaris-common/prebuild/google/products/gms_minimal.mk 注释掉这里干掉Google Play。可以不改。 3、这里讲UVC配置为模块了,请注意UVC所需要的模块的加载顺序。 Z:\home\wwt\uvc_r16_project\android\device\softwinner\astar-evb20\init.sun8i.rc 默认为: #csi module insmod /system/vendor/modules/videobuf-core.ko insmod /system/vendor/modules/videobuf-dma-contig.ko insmod /system/vendor/modules/cam_detect.ko # insmod /system/vendor/modules/actuator.ko # insmod /system/vendor/modules/ad5820_act.ko insmod /system/vendor/modules/cci.ko insmod /system/vendor/modules/vfe_os.ko insmod /system/vendor/modules/vfe_subdev.ko insmod /system/vendor/modules/gc0307.ko # insmod /system/vendor/modules/ov2035.ko insmod /system/vendor/modules/vfe_v4l2.ko 修改为: #csi module # /dev/video0 ov5640 insmod /system/vendor/modules/videobuf-core.ko insmod /system/vendor/modules/videobuf-dma-contig.ko #insmod /system/vendor/modules/cam_detect.ko insmod /system/vendor/modules/cci.ko insmod /system/vendor/modules/vfe_os.ko insmod /system/vendor/modules/vfe_subdev.ko insmod /system/vendor/modules/ov5640.ko insmod /system/ven
全志R16平台 parrotv1.1(Android4.4.2) /* * Store information about the video data format. */ static struct sensor_format_struct { __u8 *desc; //__u32 pixelformat; enum v4l2_mbus_pixelcode mbus_code;//linux-3.0 struct regval_list *regs; int regs_size; int bpp; /* Bytes per pixel */ } sensor_formats[] = { //{ // .desc = "YUYV 4:2:2", // .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8,//linux-3.0 // .regs = sensor_fmt_yuv422_yuyv, // .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_yuyv), // .bpp = 2, //}, //{ // .desc = "YVYU 4:2:2", // .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8,//linux-3.0 // .regs = sensor_fmt_yuv422_yvyu, // .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_yvyu), // .bpp = 2, //}, { .desc = "UYVY 4:2:2", .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8,//linux-3.0 .regs = sensor_fmt_yuv422_uyvy, .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_uyvy), .bpp = 2, }, //{ // .desc = "VYUY 4:2:2", // .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8,//linux-3.0 // .regs = sensor_fmt_yuv422_vyuy, // .regs_size = ARRAY_SIZE(sensor_fmt_yuv422_vyuy), // .bpp = 2, //}, //{ // .desc = "Raw RGB Bayer", // .mbus_code = V4L2_MBUS_FMT_SBGGR8_1X8,//linux-3.0 // .regs = sensor_fmt_raw, // .regs_size = ARRAY_SIZE(sensor_fmt_raw), // .bpp = 1 //}, }; #define N_FMTS ARRAY_SIZE(sensor_formats) /* * Then there is the issue of window sizes. Try to capture the info here. */ static struct sensor_win_size sensor_win_sizes[] = { // /* UXGA */ // { // .width = UXGA_WIDTH, // .height = UXGA_HEIGHT, // .hoffset = 0, // .voffset = 0, // .regs = sensor_uxga_regs, // .regs_size = ARRAY_SIZE(sensor_uxga_regs), // .set_size = NULL, // }, //// /* 720p */ //// { //// .width = HD720_WIDTH, //// .height = HD720_HEIGHT, //// .hoffset = 0, //// .voffset = 0, //// .regs = Gc2015_sensor_hd720_regs, //// .regs_size = ARRAY_SIZE(Gc2015_sensor_hd720_regs), //// .set_size = NULL, //// }, // /* SVGA */ // { // .width = SVGA_WIDTH, // .height = SVGA_HEIGHT, // .hoffset = 0, // .voffset = 0, // .regs = sensor_svga_regs, // .regs_size = ARRAY_SIZE(sensor_svga_regs), // .set_size = NULL, // }, // /* VGA */ // { // .width = VGA_WIDTH, // .height = VGA_HEIGHT, // .hoffset = 0, // .voffset = 0, // .regs = sensor_vga_regs, // .regs_size = ARRAY_SIZE(sensor_vga_regs), // .set_size = NULL, // }, /* VGA */ { .width = VGA_WIDTH, .height = VGA_HEIGHT, .hoffset = 0, .voffset = 0, .regs = sensor_default_regs, .regs_size = ARRAY_SIZE(sensor_default_regs), .set_size = NULL, }, };
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值