android v2.2 linux v2.6.32 board DM3730 sensor ov2656 参考: 《Android 底层开发技术实战详解》 在OMAP平台中,可以使用高级的ISP(图像信号处理)模块通过外接i2c方式连接的Camera Sensor驱动 来获得视频帧的数据 drivers/media/video/ 此目录主要由三个部分组成 video for linux 2 设备:实现文件是omap34xxcam.h和omap34xxcam.c ISP: 实现文件是isp目录中的isp.c isph3a.c isppreview.c ispresizer.c提供通过ISP进行 3A 预览 改变大小等功能 Camera Sensor 驱动:使用v4l2-int-device结果来注册 1.注册i2c主设备和平台数据的设置 arch/arm/mach-omap2/board-xxx //注册i2c 2 时钟为400kHz omap_register_i2c_bus(2, 400, xxx_i2c_boardinfo, ARRAY_SIZE(xxx_i2c_boardinfo)); #define OV2656_I2C_ADDR (0x61 >> 1) static struct i2c_board_info __initdata xxx_i2c_boardinfo[] = { { I2C_BOARD_INFO("ov2655", OV2655_I2C_ADDR), .platform_data = &xxx_ov2656_platform_data, }, }; struct ov2656_platform_data xxx_ov2656_platform_data = { .power_set = ov2656_sensor_power_set, .priv_data_set = ov2656_sensor_set_prv_data, .set_xclk = ov2656_sensor_set_xclk, }; 2.sensor 电源设置 DM3730 GPIO_CAM_RST------GPIO_126 GPIO_CAM_RDN------GPIO_167 static int ov2656_sensor_power_set(struct v4l2_int_device *s, enum v4l2_power power) { ---------------- switch (power) { case V4L2_POWER_ON: isp_configure_interface(vdev->cam->isp, &ov2656_if_config); if (previous_power == V4L2_POWER_OFF) { /* 打开模拟电源 */ twl_i2c_write_u8(xxx); twl_i2c_write_u8(xxx); gpio_direction_output(GPIO_CAM_PDN, 0); gpio_direction_output(GPIO_CAM_RST, 0); mdelay(1); gpio_direction_output(GPIO_CAM_RST, 1); mdelay(1); } break; case V4L2_POWER_OFF: /* 下电时序*/ twl_i2c_write_u8(xxx); break; } 3. OMAP3 isp 配置设置 drivers/media/video/isp/isp.c int isp_configure_interface(struct device *dev, struct isp_interface_config *config) 根据相关的参数配置ISP 控制器 I/F 选择并行或串行硬件preview 数据通道shifter 像素时钟极性 8到16位的CCDC输入模式 isp_configure_interface(vdev->cam->isp, &ov2656_if_config); /* *@dataline_shift: Data lane shifter. * 0 - No Shift, 1 - CAMEXT[13 to 2]->CAM[11 to 0] * 2 - CAMEXT[13 to 4]->CAM[9 to 0] * 3 - CAMEXT[13 to 6]->CAM[7 to 0] */ static struct isp_interface_config ov2656_if_config = { .ccdc_par_ser = ISP_PARLL, .dataline_shift = 0x1, .hsvs_syncdetect = ISPCTRL_SYNC_DETECT_VSRISE, .strobe = 0x0, .prestrobe = 0x0, .shutter = 0x0, .wenlog = ISPCCDC_CFG_WENLOG_AND, .wait_hs_vs = 2, .u.par.par_bridge = 0x3, .u.par.par_clk_pol = 0x0, //0 - 不反向 }; 4. ov2656 driver /drivers/media/video/ov2656.c 1)注册,删除i2c驱动 static int __init ov2656_init(void) { i2c_add_driver(&ov2656_i2c_driver); return 0; } static void __exit ov2656_exit(void) { i2c_del_driver(&ov2656_i2c_driver); } 2)定义sensor i2c驱动 static struct i2_driver ov2656_i2c_driver = { .driver = { .name = "ov2656", .owner = THIS_MODULE, }, .probe = ov2656_probe, .remove = ov2656_remove, .id_table = ov2656_id, }; static const struct i2c_device_id ov2656_id[] = { {"ov2656", 0}, {}, }; 3)注册sensor为i2c 客户端和V4L2设备 static int ov2656_probe(struct i2c_client *client, const struct i2c_device_id *id) { --------------------------- i2c_get_clientdata(client); //回调平台数据 sensor->pdata->power_set = pdata->power_set; sensor->pdata->set_xclk = pdata->set_xclk; sensor->pdata->priv_data_set = pdata->priv_data_set; 设置sensor的默认配置 sensor->timeperframe.numerator = 1; sensor->timeperframe.denominator = 15; sensor->pix.width = 640; sensor->pix.height = 480; sensor->pix.pixelformat = V4L2_PIX_FMT_YUYV; sensor->v4l2_int_device = &ov2656_int_device; ---------------------------- i2c_set_clientdata(client, sensor); v4l2_int_device_register(sensor->v4l2_int_device); } 4)V4L2 初始化设备 static struct v4l2_int_device = { .module = THIS_MODULE, .name = "ov2656", .type = v4l2_int_type_slave, .u = { .slave = &ov2656_slave, }, }; 5)V4L2初始化从设备 static struct v4l2_int_slave ov2656_slave = { .ioctls = ov2656_ioctl_desc, .num_ioctls = ARRAY_SIZE(ov2656_ioctl_desc), }; 提供给上层的ioctl接口 static struct v4l2_int_ioctl_desc ov2656_ioctl_desc[] = { }; 6)加载sensor驱动的log ------------ioctl_g_priv-------------- ------------ioctl_s_power-------------- ------------ioctl_dev_init-------------- ov2656 2-0030: Detect success (26,56) ------------ioctl_g_fmt_cap-------------- ------------ioctl_s_power-------------- //返回sensor的私有数据地址 static int ioctl_g_priv(struct v4l2_int_device *s, void *p) { struct ov2656_sensor *sensor = s->priv; return sensor->pdata->priv_data_set(s, p); return 0; } //电源设置 static int ioctl_s_power(struct v4l2_int_device *s, enum v4l2_power new_power) { ----------- switch (new_power) { case V4L2_POWER_ON: rval = sensor->pdata->set_xclk(s, OV2656_XCLK);//24M camera时钟 if (rval == -EINVAL) break; rval = sensor->pdata->power_set(s, V4L2_POWER_ON); if (rval) break; if (sensor->detected) ov2656_configure(s); else { rval = ioctl_dev_init(s); printk("-------ioctl_s_power--->dev_init----\n"); if (rval) goto err_on; } break; case V4L2_POWER_OFF: err_on: rval = sensor->pdata->power_set(s, V4L2_POWER_OFF); sensor->pdata->set_xclk(s, 0); break; default: return -EINVAL; } //初始化设备当从设备和主设备相连的时候 static int ioctl_dev_init(struct v4l2_int_device *s) { ---------- ov2656_detect(client); --------- } //探测i2c设备,读取i2c sensor的PIDH和PIDL static int ov2656_detect(struct i2c_client *client) { u32 pidh, pidl; if (!client) return -ENODEV; if (ov2656_read_reg(client, 1, OV2656_PIDH, &pidh)) return -ENODEV; if (ov2656_read_reg(client, 1, OV2656_PIDL, &pidl)) return -ENODEV; if ((pidh == OV2656_PIDH_MAGIC) && ((pidl == OV2656_PIDL_MAGIC1) )) { dev_info(&client->dev, "Detect success (%02X,%02X)\n", pidh, pidl); return pidl; } return -ENODEV; } static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { printk("------------%s--------------\n", __func__); return 0; } 5.打开sensor的log / # ------------ioctl_s_power-------------- ----------------ov2656_configure------------ ------------ioctl_s_power-------------- ------------ioctl_g_fmt_cap-------------- ------------ioctl_enum_fmt_cap-------------- --------ioctl_enum_framesizes--frms->index=0------ ------------ioctl_enum_frameintervals-------------- ------------ioctl_enum_frameintervals-------------- --------ioctl_enum_framesizes--frms->index=1------ ------------ioctl_enum_frameintervals-------------- ------------ioctl_enum_frameintervals-------------- --------ioctl_enum_framesizes--frms->index=2------ ------------ioctl_enum_fmt_cap-------------- ------------ioctl_g_fmt_cap-------------- ------------ioctl_s_fmt_cap-------------- ------------ioctl_s_parm-------------- ------------ioctl_s_power-------------- ----------------ov2656_configure------------ 1)设置电源 2)ov2656_configure static int ov2656_configure(struct v4l2_int_device *s) { ------------------------------ //写复位寄存器 ov2656_write_reg(client, OV2656_SYS, 0x80); mdelay(5); /* Common registers */ err = ov2656_write_regs(client, ov2656_common[0]); sensor->hsize = pix->width; sensor->vsize = pix->height; /* Store image size */ sensor->width = pix->width; sensor->height = pix->height; sensor->crop_rect.left = 0; sensor->crop_rect.width = pix->width; sensor->crop_rect.top = 0; sensor->crop_rect.height = pix->height; -------------------------- } 3)ioctl_g_fmt_cap 查询出一种格式,查询驱动所支持的格式 static int ioctl_g_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { return 0; } 4)ioctl_enum_framesizes 帧大小 static int ioctl_enum_framesizes(struct v4l2_int_device *s, struct v4l2_frmsizeenum *frms) { if (frms->index >= 2) return -EINVAL; frms->pixel_format = V4L2_PIX_FMT_YUYV; frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; frms->discrete.width = 640; frms->discrete.height = 480; } 5)ioctl_enum_frameintervals 每秒15帧 static int ioctl_enum_frameintervals(struct v4l2_int_device *s, struct v4l2_frmivalenum *frmi) { if (frmi->index >= 1) return -EINVAL; frmi->type = V4L2_FRMIVAL_TYPE_DISCRETE; frmi->discrete.numerator = 1 ; frmi->discrete.denominator = 15; return 0; } 6)ioctl_s_fmt_cap 设置格式 static int ioctl_s_fmt_cap(struct v4l2_int_device *s, struct v4l2_format *f) { struct ov2656_sensor *sensor = s->priv; struct v4l2_pix_format *pix = &f->fmt.pix; sensor->pix = *pix; return 0; } 7)ioctl_s_parm 设置参数 static int ioctl_s_parm(struct v4l2_int_device *s, struct v4l2_streamparm *a) { printk("------------%s--------------\n", __func__); return 0; } 8)寄存器配置列表 const static struct ov2656_reg ov2656_common[2][300] = { /* XGA_Default settings */ { //IO & Clock & Analog Setup {0x308c,0x80}, //TMC12: DIS_MIPI_RW {0x308d,0x0e}, //TMC13: MIPI disable {0x360b,0x00}, {0x30b0,0xff}, //IO_CTRL0: Cy[7:0] {0x30b1,0xff}, //IO_CTRL1: C_VSYNC,C_STROBE,C_PCLK,C_HREF,Cy[9:8] {0x30b2,0x2c}, //IO_CTRL2: R_PAD[3:0] {0x300f,0xa6}, {0x3010,0x81}, {0x3082,0x01}, {0x30f4,0x01}, {0x3091,0xc0}, {0x30ac,0x42}, {0x30d1,0x08}, {0x3015,0x02}, //AUTO_3: AGC ceiling = 4x, 5dummy frame {0x3093,0x00}, {0x307e,0xe5}, //TMC8: AGC[7:6]=b'11 {0x3079,0x00}, {0x3017,0x40}, //AUTO_5: disable data drop, manual banding counter=0 {0x30f3,0x82}, {0x306a,0x0c}, //0x0c->0x0f Joe 0814 : BLC {0x306d,0x00}, {0x336a,0x3c}, {0x3076,0x6a}, //TMC0: VSYNC drop option: drop {0x30d9,0x95}, {0x3601,0x30}, {0x304e,0x88}, {0x30f1,0x82}, {0x306f,0x14}, {0x302a,0x02}, {0x302b,0x6a}, {0x3012,0x10}, {0x3018,0x80}, // jerry, 0624 {0x3019,0x70}, {0x301a,0xd4}, //Wonder 20090909 {0x3013,0xf7}, {0x30af,0x10}, {0x304a,0x00}, {0x304f,0x00}, {0x30a3,0x80}, //Wonder From OV, 20090615 0x10 {0x3013,0xf7}, {0x3014,0xa4}, //R1D bit6 always = 0 , bit[5]=1, bit[0]=1 //Wonder for AE oscillation {0x3071,0x00}, {0x3073,0x00}, {0x304d,0x42}, {0x304a,0x00}, //Disable 50/60 auto detection function, due to ov2650 no this function {0x304f,0x00}, //Disable 50/60 auto detection function, due to ov2650 no this function {0x3095,0x07}, {0x3096,0x16}, {0x3097,0x1d}, {0x3020,0x01}, {0x3021,0x18}, {0x3022,0x00}, {0x3023,0x06}, {0x3024,0x06}, {0x3025,0x58}, {0x3026,0x02}, {0x3027,0x5e}, {0x3088,0x02},// ISP_XOUT 640 {0x3089,0x80}, {0x308a,0x01},// ISP_YOUT 480 {0x308b,0xe0}, {0x3316,0x64}, {0x3317,0x25}, {0x3318,0x80}, {0x3319,0x08}, {0x331a,0x64}, {0x331b,0x4b}, {0x331c,0x00}, {0x331d,0x38}, {0x3100,0x00}, {0x3320,0xfa}, //Jerry 0x9a {0x3321,0x11}, {0x3322,0x92}, {0x3323,0x01}, {0x3324,0x97}, //Jerry 0x92 {0x3325,0x02}, {0x3326,0xff}, {0x3327,0x10}, //0x0c {0x3328,0x11}, //Jerry 0x0f {0x3329,0x16}, //Jerry 0x14 {0x332a,0x59}, //Jerry 0x66 {0x332b,0x60}, //5f -> 5c //Jerry 0x5c {0x332c,0xbe}, //a5 -> 89 //Jerry 0x89 {0x332d,0x9b}, //ac -> 96 //Jerry 0x96 {0x332e,0x34}, //35 -> 3d //Jerry 0x3d {0x332f,0x36}, //Jerry 0x2f {0x3330,0x49}, //Jerry 0x57 {0x3331,0x44}, //Jerry 0x3d {0x3332,0xf0}, {0x3333,0x00}, //0x10 {0x3334,0xf0}, {0x3335,0xf0}, {0x3336,0xf0}, {0x3337,0x40}, {0x3338,0x40}, {0x3339,0x40}, {0x333a,0x00}, {0x333b,0x00}, {0x3380,0x27}, {0x3381,0x5c}, {0x3382,0x0a}, {0x3383,0x29}, //0x2a {0x3384,0xab}, //0xad {0x3385,0xd3}, //d5 {0x3386,0xbf}, {0x3387,0xbc}, {0x3388,0x03}, {0x3389,0x98}, {0x338a,0x01}, {0x3340,0x0c}, {0x3341,0x18}, {0x3342,0x30}, {0x3343,0x3d}, {0x3344,0x4b}, {0x3345,0x59}, //0x60 {0x3346,0x67}, //0x6a {0x3347,0x71}, {0x3348,0x7d}, //0x84 {0x3349,0x8e}, //0x96 {0x334a,0x9b}, //0xa2 {0x334b,0xa6}, //0xac {0x334c,0xb9}, {0x334d,0xc6}, {0x334e,0xd9}, {0x334f,0x34}, {0x3350,0x35}, //Wonder for lens 20090909 {0x3351,0x25}, {0x3352,0x08}, {0x3353,0x23}, {0x3354,0x00}, {0x3355,0x85}, {0x3356,0x34}, {0x3357,0x25}, {0x3358,0x08}, {0x3359,0x1e}, {0x335a,0x00}, {0x335b,0x85}, {0x335c,0x35}, {0x335d,0x25}, {0x335e,0x08}, {0x335f,0x18}, {0x3360,0x00}, {0x3361,0x85}, {0x3363,0x01}, {0x3364,0x03}, {0x3365,0x02}, {0x3366,0x00}, {0x338b,0x0C}, //auto uv //add saturation {0x338c,0x10}, {0x338d,0x80}, //40 indoor saturation {0x3370,0xd0}, {0x3371,0x00}, {0x3372,0x00}, {0x3374,0x10}, {0x3375,0x10}, {0x3376,0x04}, ///0624,jerry, for preview sharp {0x3377,0x00}, {0x3378,0x04}, {0x3379,0x40}, {0x3069,0x80}, //Jerry {0x3087,0x02}, {0x3300,0xfc}, {0x3302,0x11}, {0x3400,0x00}, {0x3606,0x20}, {0x3601,0x30}, {0x30f3,0x83}, {0x304e,0x88}, {0x3086,0x0f}, {0x3086,0x00}, // {0x30a8,0x54}, //0x56 Wonder for Sun black {0x30aa,0x52}, //0x42 //Wonder for Sun black {0x30af,0x10}, {0x30b2,0x2c}, {0x30d9,0x8c}, {0x363B,0x01}, {0x363C,0xF2}, {0xFFFF,0xFF}, }, }; 6.android HAL hardware/ti/omap3/camera/V4L2Camera.cpp CameraHardware.cpp Converter.cpp 在startPreview()的实现在,保存预览回调函数并建立预览线程,在预览线程的循环中,等待视频数据的到达 视频数据到达后调用预览回调函数,将视频帧送出 status_t CameraHardware::startPreview() { ---------- mCamera->StartStreaming(); mPreviewThread = new PreviewThread(this); ---------- } 取景器预览的主要步骤 1)在初始化的过程中,建立预览数据的内存队列 2)在startPreview()中建立预览线程 3)在预览线程的循环中,等待视频数据到达 int CameraHardware::previewThread() { ----------VIDEO_FRAME--------- yuyv422_to_yuv420sp((unsigned char *)rawFramePointer, (unsigned char *)mRecordingHeap->getBase(), PREVIEW_WIDTH, PREVIEW_HEIGHT); mDataCb(CAMERA_MSG_VIDEO_FRAME, mRecordingBuffer, mCallbackCookie); mDataCbTimestamp(systemTime(), CAMERA_MSG_VIDEO_FRAME, mRecordingBuffer, mCallbackCookie); --------------PREVIEW FRAME------------ yuyv422_to_yuv420sp((unsigned char *)rawFramePointer, (unsigned char *)mHeap->getBase(), PREVIEW_WIDTH, PREVIEW_HEIGHT); mDataCb(CAMERA_MSG_PREVIEW_FRAME, mBuffer, mCallbackCookie); ------------------------------------------------------- 4)在预览到达后使用预览回调机制将视频向上传送 int CameraHardware::pictureThread() { ----------------- if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { LOGE("Take Picture COMPRESSED IMAGE"); mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mCamera->GrabJpegFrame(), mCallbackCookie); } ------------------ } 录制视频的主要步骤 1)在startRecording()的实现中保存录制视频回调函数 2)录制视频可以使用自己的线程,也可以使用预览线程 status_t CameraHardware::startRecording() { } 3)通过录制回调函数将视频帧送出 void CameraHardware::releaseRecordingFrame(const sp<IMemory>& mem) { } 当调用releaseRecordingFrame()后,表示上层通知Camera 硬件抽象层,这一帧的内存 已经用完,可以进行下一次的处理。 camera 处理流程 1)使用映射内核内存的方式V4L2_MEMORY_MMAP,构建预览的内存MemoryHeapBase需要 从V4L2驱动程序中得到内存指针 status_t CameraHardware::startPreview() { ----------------- mPreviewFrameSize = PREVIEW_WIDTH * PREVIEW_HEIGHT * 2; mHeap = new MemoryHeapBase(mPreviewFrameSize); ---------------- } 2)在预览线程中,使用VIDIOC_DQBUF调用阻塞等待视频帧的到来,处理完成后使用VIDIOC_QBUF调用 将帧内存再次压入队列,然后等待下一帧的到来 ioctl(fd, VIDIOC_DQBUF, &videoIn->buf); ioctl(fd, VIDIOC_QBUF, &videoIn->buf); 7. CameraHardware.cpp 打开摄像头 E/CameraHardware( 913): return Preview Heap E/CameraHardware( 913): beginAutoFocusThread E/CameraHardware( 913): Picture Thread E/CameraHardware( 913): Take Picture RAW IMAGE E/CameraHardware( 913): Take Picture COMPRESSED IMAGE E/CameraHardware( 913): pictureThread: preview started 调用 sp<IMemoryHeap> CameraHardware::getPreviewHeap() const { LOGE("return Preview Heap"); return mPreviewHeap; } 按下拍照按键 int CameraHardware::beginAutoFocusThread(void *cookie) { CameraHardware *c = (CameraHardware *)cookie; LOGE("beginAutoFocusThread"); return c->autoFocusThread(); } status_t CameraHardware::takePicture() { pictureThread(); return NO_ERROR; } int CameraHardware::pictureThread() { --------------------- if (mMsgEnabled & CAMERA_MSG_COMPRESSED_IMAGE) { LOGE("Take Picture COMPRESSED IMAGE"); mDataCb(CAMERA_MSG_COMPRESSED_IMAGE, mCamera->GrabJpegFrame(), mCallbackCookie); LOGE("%s: preview started", __FUNCTION__); mPreviewThread = new PreviewThread(this); } 调用GrabJpegFrame()在V4L2Camera.cpp中实现