现在的位置: 首页 > 综合 > 正文

Camera模块解析之驱动篇 .

2014年01月26日 ⁄ 综合 ⁄ 共 17354字 ⁄ 字号 评论关闭

1  手机摄像头功能概述

手机摄像头功能由多个功能模块组成,主要三个部分,采集,加工,显示。

(1)采集部分由感光的sensor完成,通过CAM IF接口与手机芯片内的CAM连接。

(2)CAM对CAM IF数据进行加工,主要是格式转换,特殊效果等。最终处理出来的一帧数据,存在内存中。

(3) 手机的刷新线程,使用手机内部的DMA功能,或者OVERLAY技术,把处理好的camera图像,显示到LCD上。刷新部分,不在camera框架范围内,后面只做简单讨论。

图1:Camera典型硬件模块图

2  Sensor简介

Sensor是对图像的采集系统,通常采用的是ov系列的芯片。如ov2655等。通常包含两路接口:

(1)控制总线:Sensor也是一个智能嵌入式系统,一般通过I2C总线与手机芯片通信。手机可以通过I2C读写Sensor的寄存器,改变Sensor的参数,从而改变其工作方式。

(2)数据总线:Sensor通过CAM IF接口与CAM联系。

 

图2:sensor硬件连接图

 

由图可知,sensor工作的条件需要:

(1)电压供应,一般模拟电压,数字电压。

(2)工作时钟,通常为24M HZ的正弦波。一般为手机芯片产生

(3)SDA,SCL,i2c总线连接,sensor通常为从设备。

(4)standby控制线,手机芯片通过这条GPIO控制线,控制sensor的工作是否开启。

(5)Sensor输出给手机芯片的接口,CAM IF接口:

(6)并行数据线,通常8位,10位。分辨率高的sensor数据线需要更多。

(7)提供给手机芯片内集成的camera模块的PCLK,HCLK,VCLK.(像素同步信号,行同步信号,帧同步信号)。

Sensor通常产出稳定频率的数据图像流,手机芯片可以通过I2C总线接口,修改寄存器,改变帧频率。也可以改变sensor的输出流的格式,通常采用yuv422格式。

3  CAM简介

CAM就是将Sensor采集过来的数据,转换相应格式,及其他加工,最后存放到内存中。CAM核心就是个DSP。这个阶段,dsp可以做很多图像处理的事情。比如颜色纠正,自动对焦,scaler等。不同平台会有不同。

由于Sensor的核心也是dsp,对于这些特效工作,一般Sensor中也会提供。

高通平台的Sensor的特效(反色)就可以在Sensor中设置。

由图可知

CAM consists ofthe following elements:

• Image Signal Processing (ISP)1

• Color Processing

• Image effects

• Luminance / Chrominance Splitter (Y/C Split)

• Resize (Down/Up-Scalers)

• JPEG Encoder

• YCbCr to RGB conversion for preview

• Memory Interface

• Control Unit

具体详解,可见ste6715 datesheet。

这些模块看似很复杂,对于我们开发者来说,也不复杂。我们知道它们相应的流程,并且知道每个子模块提供了什么功能。这些模块的功能可以通过相应模块的寄存器进行相应调整的]。这同sensor的参数调整的思想是一样的。硬件提供功能,通过寄存器进行参数调整。

 

Camera的native层软件接口,在Camera在native层中,提供了/dev/video*的设备节点。Native层通过打开设备文件,关联上camera,申请一串帧缓冲区,建立循环队列,并把这些内存地址传给内核的camera模块,并等待内核camera的处理结束。Camera模块一帧处理结束,native层就会返回。

4  LCD显示

LCD的显示,就是将lcd的framebuffer的数据映射到LCD屏上,而我们camera的数据要在屏幕上显示,就只需建立camera帧buffer到framebuffer的映射关系。可以使用内核的DMA,也可以使用overlay。通常的preview过程都是在native层以上开个线程,waiting kernel的处理完成,然后push到lcd屏上,如此循环。

(1)软件设计思想

5  V4l2驱动框架:

关联文件:V4l2-dev.c(src\linux\kernel\linux\drivers\media\video)

Videodev2.h(src\linux\kernel\linux\include\linux) Cam-core分析

V4l2.c(src/linux/modules/v4l2cam)

V4L2是linux的标准接口,提供了众多的标准IOCTL接口,这样不管内核驱动如何改变,风格各异,都可以让应用程序native程序稳定工作。IOCTL接口标准定义于Videodev2.h,这个文件也会被android系统所引用。

V4L2层的意义在于:让平台的驱动,通过char字符设备层能够与应用关联起来。首先对v4l2.c和v4l2-dev.c两个文件的内容做个简单介绍:

(1)V4l2.c文件主要工作:在模块加载的时候,调用v4l2_init()函数,该函数完成camera_sensor的获取和对video_device设备的创建,初始化和注册。实际上完成一个video_device设备驱动,最重要的是v4l2_ioctrl()函数的实现,根据android的HAL层传下来的操作类型调用不同的控制函数,而这些控制函数通过调用cameraa_sensor和camIF接口来实现。

(2)V4l2-dev.c文件的主要工作:完成一个字符设备驱动,并实现了video_device注册的工作。字符设备驱动中的主要工作是通过调用video_device设备驱动来完成的。

这里请注意:camera驱动分为三个部分,最后生成cam.ko,v4l2cam.ko和sensor.ko三个模块,v4l2cam.ko依赖于cam.ko和sensor.ko模块,因此,v4l2cam.ko后于cam.ko和sensor.ko模块加载。

下面来分析v4l2框架的工作流程。

(1)v4l2-dev.c

v4l2-dev.c文件中初始化函数申请了v4l2的字符设备号,但是并没有注册和关联具体驱动。

static int __init videodev_init(void)
{
  dev_t dev =MKDEV(VIDEO_MAJOR, 0);
  int ret;
……
  ret =register_chrdev_region(dev, VIDEO_NUM_DEVICES, VIDEO_NAME);  //申请一组设备号
……
  ret =class_register(&video_class);              //注册一个类设备
}

 

提供了2个函数供其他具体驱动进行注册

video_register_device_index();

video_register_device();

int video_register_device_index(struct video_device *vdev, int type,int nr,
                              intindex)
{
……
  vdev->cdev= cdev_alloc();
  if(vdev->cdev == NULL) {
         ret =-ENOMEM;
         gotocleanup;
  }
  if(vdev->fops->unlocked_ioctl)
         vdev->cdev->ops= &v4l2_unlocked_fops;
  else
         vdev->cdev->ops= &v4l2_fops;
  vdev->cdev->owner= vdev->fops->owner;
  ret = cdev_add(vdev->cdev,MKDEV(VIDEO_MAJOR, vdev->minor), 1);  //注册一个字符设备
……
memset(&vdev->dev, 0, sizeof(vdev->dev));
  /* The memsetabove cleared the device's drvdata, so
     put back the copy we made earlier. */
  video_set_drvdata(vdev,priv);
  vdev->dev.class= &video_class;
  vdev->dev.devt= MKDEV(VIDEO_MAJOR, vdev->minor);
  if(vdev->parent)
         vdev->dev.parent= vdev->parent;
  dev_set_name(&vdev->dev,"%s%d", name_base, vdev->num);
  ret = device_register(&vdev->dev);  //注册video_deice设备,将其添加到sysfs文件系统
……
mutex_lock(&videodev_lock);
  video_device[vdev->minor] = vdev;   //本地管理的一个video_device数组
  mutex_unlock(&videodev_lock);
……
}

该文件中核心对象为:static struct video_device *video_device[VIDEO_NUM_DEVICES];设备文件就是根据index与相应的video_device[index]指针关联。

      在videodev_init()初始化函数中,申请了一组设备号,并注册了一个类video_class,在注册视频设备时,首先注册了一个字符设备,然后用相同的设备号注册了一个设备节点。这里的字符设备是让平台的驱动通过char字符设备层能够与应用关联起来,V4l2框架核心文件为v4l2-dev.c。 

v4l2-dev.c文件的核心对象为:static struct video_device *video_device[VIDEO_NUM_DEVICES],它维护每个注册了的video_device设备,设备文件就是根据index与相应的video_device[index]指针关联。所以,这个文件的主要工作就是:字符设备的驱动内容。

字符设备驱动最后还是调用video_device的fops来实现的,这个fops就是v4l2.c中的cam_fops结构体,不过它只实现了打开,关闭,映射和io控制四个函数。这里要注意的是:在注册video_device前是通过config_a_device()来初始化video_device的fops的成员的。

 

(2)V4l2.c:

文件中重要的数据对象:

struct acq_device_t {
       structvideo_device *vfd;           //视频设备对象指针
#defineNAME_LENGTH 16
    char name[NAME_LENGTH];
       ……
        */
       structacq_session_cxt_t *streaming;              //打开camera的一个上下文
       video_frame_t *stream_vdf[V4L2_MAX_VDF];
 
       /* Hardware dependant parts */
       /* The Camera object plugged to our CAMIF*/
       structcamera_sensor *camera;        //sensor对象指针
       /* specific to sensor */
       void *camera_priv_data;
       ……
};

 

struct acq_session_cxt_t {   //代表一个打开的camera设备
       structacq_device_t *dev;
       /* handle to control session of CAM hardware bloc */
       void *camhdl;
       /* data pool handle (framemem) */
       void *pool_hdl;
       /* jpeg buffers */
#ifdef CONFIG_V4L2CAM_PMEM
       memblock_t blocks[V4L2_MAX_BUF];
#else
       struct v4l2_buffer bufs[V4L2_MAX_BUF];
#endif
       /* CameraPixfmt defines the size andpixel format of the sensor. */
       struct v4l2_pix_format CameraPixfmt;
       /* CamPixfmt defines the size and pixelformat at the output of CAM bloc */
       struct v4l2_pix_format CamPixfmt;
       /* CamPixfmt defines the size and pixelformat of thumbnail at the output of CAM bloc*/
       struct v4l2_pix_format CamThumbfmt;
       /* video frame use to preview session */
       video_frame_t *cur_vdf;
       video_frame_t *next_vdf;
       int count;
};

 

V4l2cam驱动模块的初始化函数流程如下:

int v4l2_init(void)
{
……
  while (1) {
         dev =kzalloc(sizeof(struct acq_device_t), GFP_KERNEL);
         dev->camera = v4l2_detect(dev);             //检测并获得camera_sensor设备
         /* openthe cam bloc interface */
         cam_open(&hdl);
         /* wegot a camera plugged ! */
         /*initialiseprivate fields of the sensor, for cmos coprocessor we try to find the cameraplugged to it */
         dev->camera_priv_data= dev->camera->init();
 
         /*close the cam bloc interface */
         cam_close(hdl);
         dev->vfd = video_device_alloc();              //为video_device分配内存
         if (config_a_device(dev)) {                //初始化video_device设备结构,包括fops成员
                unconfig_a_device(dev);
                ret= -ENODEV;
                gotobail;
         }
 
         if (video_register_device(dev->vfd,VFL_TYPE_GRABBER, dev->vfd->minor) != 0) {                                //注册video_device设备
                CRITICAL("Couldn'tregister video driver.");
                unconfig_a_device(dev);
                ret= -ENODEV;
                gotobail;
         }
         dev->is_registered= 1;
         dev->preview_running= FALSE;
         dev->snapshot_running= FALSE;
         dev->snapshot_done= FALSE;
 
         /* initcompletion */
         init_completion(&dev->complete);
 
         video_set_drvdata(dev->vfd, dev);
}
}

该函数中出现一个config_a_device(dev)函数,这个函数是video_device设备的初始化配置函数,包括对文件操作指针的赋值,这里是一个初始化封装。

先来看看v4l2_detect()函数:

structcamera_sensor *v4l2_detect(structacq_device_t *dev)
{
       struct camera_sensor **Cams = NULL;
       struct camera_sensor *cam = NULL;
       void *hdl = 0;
       int i = 0;
       PROLOG("");
 
       dev->camera = NULL;
       cam_open(&hdl);
 
       Cams= sensor_get_cameras();         //获取camera_sensor数组
       if (Cams) {
              while (Cams[i] != NULL) {
                     cam = Cams[i];
                     if (!cam->isProbed) {
                            cam->isProbed =1;
                            if (cam->detect() == 0) {            //创建和初始化camera_sensor就在这里,这是sensor驱动部分的内容,后面会有详细的介绍
                                   gotodetect_exit;
                            }
                     }
                     i++;
              }
       }
       dev->camera = NULL;
       cam = NULL;
 
      detect_exit:
       cam_close(hdl);
       EPILOG("");
       return cam;
}

该函数中调用了cam->detect()函数,这个函数在camera的sensor部分被实现,后面会有详细说明,下面看看如何注册video_device设备的,video_register_device()函数,该函数代码在前面已经列出,这里简单描述:

intvideo_register_device(struct video_device *vdev, int type, int nr)
{
       return __video_register_device(vdev,type, nr, 1);
}

 

static int__video_register_device(struct video_device *vdev, int type, int nr,
              int warn_if_nr_in_use)
{
       ……
       ret= cdev_add(vdev->cdev, MKDEV(VIDEO_MAJOR, vdev->minor), 1);
       ……
       ret= device_register(&vdev->dev);
       ……
       video_device[vdev->minor]= vdev;
       ……
}

该文件中核心对象为:static struct video_device *video_device[VIDEO_NUM_DEVICES];设备文件就是根据index与相应的video_device[index]指针关联。

由驱动框架我们了解到,平台驱动就是初始化video_device结构,然后注册到V4L2框架中。在V4l2框架中有几个重要内容有必要提出来:

(1)capture_callback()

voidcapture_callback(void *userdata)
{
 struct acq_session_cxt_t*acq_cxt = (struct acq_session_cxt_t *)userdata;
 struct acq_device_t *dev;
 BUG_ON(!acq_cxt);
 dev = acq_cxt->dev;
 BUG_ON(!dev);
 dev->it_frame_nb++;
 
 if (dev->wait_end_of_frame){
 
        dev->wait_end_of_frame= 0;
        complete(&dev->complete);
 } else {
        if(dev->wait_first_frame) {
               dev->wait_first_frame= 0;
               complete(&dev->complete);
        }
 }

}

这是一个回调函数,当一帧数据完成时候,中断函数会响应,从而callback函数也会被调用,唤醒comple等待的函数。此callback函数通过cam_streaming_start函数,注册到底层cam驱动中,下面是启动camera数据流的函数。

static inlineint v4l2_stream_on(structacq_device_t *dev, struct acq_session_cxt_t *acq_cxt, void *arg)
{
……
cam_streaming_start(acq_cxt->camhdl,
                         acq_cxt->cur_vdf, (void*)capture_callback, (void *)data_loss_callback, (void *)acq_cxt);
……
}
 

(2)v4l2_do_ioct()

static long v4l2_do_ioctl(struct file *file, unsigned intcmd, void *arg);

此函数相当庞大,在此不列出了,就是IOCTL的实现函数,每个命令都会有个处理函数,也在此文件中。

(3)v4l2_mmap()

static int v4l2_mmap(struct file *file, structvm_area_struct *vma);

此函数把内核的cam数据映射到用户空间,用户空间可以读取。

(4)v4l2_open()

static int v4l2_open(struct file *file)

此函数就是创建一次与具体sensor的连接,重要的是创建了acq_session_cxt_t上下文对象。但此时cam的采集转换工作并没有启动。启动工作是通过ioctl在v4l2_do_ioctl调用v4l2_stream_on函数启动的,就是刚才的那个设置回调函数的函数。

这里的核心对象为:struct acq_device_t *devices[] = { NULL, NULL, NULL, NULL, NULL,NULL, NULL, NULL };每个 acq_device_t 标识唯一camera设备,而acq_session_cxt_t标识一次打开的上下文。

V4L2平台驱动,调用了2个子模块驱动:Sensor驱动和cam模块驱动,使两者协调工作,从而组成个完成的摄像头工作模块。不过这里要注意的是,v4l2.c 指出同一时间只能打开一次,不能重复打开。

6  Sensor驱动部分:

(一)sensor的核心部分

Sensor-core驱动很简单。此文件维护一个camera_sensor数组,以供v4l2.c使用。它还实现了关联sensor对象的i2c驱动句柄对象(camera_serial_bus类型)。

初始化函数流程:

intsensor_init(void)
{
/* +LMSqc15648 Update i2c mechanism to avoid static registering in kernel */
#ifdef CONFIG_I2C_NEW_PROBE
  struct i2c_board_info board_info = {
         type :"i2ccamera",
         addr :0x30
  };
    struct i2c_adapter* i2c_adap=NULL;
  PROLOG("");
 
    i2c_adap = i2c_get_adapter(1);            //获取一号总线适配器
    if( i2c_adap==NULL ){
       CRITICAL("Can't get i2cadapter");
    }else{   
      i2c_new_device(i2c_adap, &board_info);  //创建一个新i2c设备
}
#endif
/* -LMSqc15648 Update i2c mechanism to avoid static registering in kernel */
  EPILOG("");
  return 0;
}

       这个函数主要是根据borad_info创建了一个i2c_client设备,这在后面的i2c_init()函数被调用时会匹配到该设备。

       文件中有一个camera_sensor数组,用来保存每个sensor实例:

structcamera_sensor *Cams[] = {
       &camera_gc2015,
       NULL
};

获取sensor数组的重要函数:

structcamera_sensor **sensor_get_cameras()
{
return Cams;
}

       该文件的主要工作还是实现了一个camera_serial_bus结构对象camera_sbus_i2c,它是sensor同i2c总线通信的实现。

structcamera_serial_bus camera_sbus_i2c= {
/* +LMSqc11271'probe' is not working in device driver. */
#ifdef CONFIG_I2C_NEW_PROBE
      client:NULL,
#endif
/* -LMSqc11271'probe' is not working in device driver. */
      init:i2c_init,
      cleanup:i2c_cleanup,
      set_devid:i2c_set_devid,
      read:i2c_read,
      write:i2c_write,
      read:i2c_read,
      read8:i2c_read8,
      write8:i2c_write8,
};

 

(二)sensor部分

       Sensor驱动部分的内容就是:根据sensor的datesheet填写出camera_sensor数据结构的函数实现,并创建个 camera_sensor对象,此对象会被sensor-core引用,下面以gc2015为例。

       Gc2015的驱动代码实现文件为gc2015.c,该文件中定义了一个camera_sensor对象:

struct camera_sensor camera_gc2015 = {
     name:"gc2015",
     detect:gc2015_detect,
     isProbed:0,
     clock:24,
     yuvOrder:INPUT_SEQ_CbYCrY,//INPUT_SEQ_CrYCbY,
     hsyncPol:INPUT_POL_VSYNC_HIGH,//INPUT_POL_VSYNC_HIGH,//INPUT_POL_VSYNC_LOW,
     vsyncPol:INPUT_POL_HSYNC_LOW,//INPUT_POL_HSYNC_HIGH,//INPUT_POL_HSYNC_LOW,
     sampleEdge:INPUT_SAMPLE_EDGE_POS,
     fullrange:false,
     init:gc2015_init,
     cleanup:gc2015_cleanup,
     open:gc2015_open,
     close:gc2015_close,
     enum_pixformat:gc2015_enum_pixformat,
     try_format:gc2015_try_format,
     set_format:gc2015_set_format,
     stop_sensor:gc2015_stop_sensor,
     start_sensor:gc2015_start_sensor,
     query_control:gc2015_query_control,
     get_control:gc2015_get_control,
     set_control:gc2015_set_control,
     ……
     query_wb_mode:gc2015_query_wb_mode,
     get_wb_mode:gc2015_get_wb_mode,
     set_wb_mode:gc2015_set_wb_mode,
     check_frame:gc2015_check_frame,
     check_short_circuit:NULL,
};

 

       这个文件的主要内容就是实现这些初始化函数,其中最为重要的是gc2015_detect()函数,前文有介绍过,在v4l2框架里面的v4l2.c文件的v4l2_init()函数中调用v4l2_detect()函数,它既是调用的这个gc2015_detect()函数完成对camera_sensor的检测和初始化的,下面来看看这个函数的具体实现。

static int gc2015_detect(void)
{
       externstruct camera_serial_bus camera_sbus_i2c;
       structcamera_serial_bus *sbus;
  
       u6_gpio_write_pin(GC2015_GPIO_PD,GC2015_GPIO_PD_OFF);
       msleep(100);
 
       /*set the output camera clock (camclko) */
       gc2015_set_Mclk(camera_gc2015.clock);
   
       msleep(200);
   printk("gc2015_detect\n");
       if((rc = sbus->init())) {              //该函数调用注册一个i2c驱动
              CRITICAL("Couldn'taccess I2c part ofcamera");
              gotoerror;
       }
   
       sbus->set_devid(CAM_GC2015_I2C_ID);              //重新设定sensor设备的i2c地址
 
       if((rc = gc2015_write_reglist(gc2015_init_global)))
              gotoerror;
   
       /*DeviceID*/
       if((rc = gc2015_read_reg(0x00, &pidh)))
              gotoerror;
       if((rc = gc2015_read_reg(0x01, &pidl)))
              gotoerror;
 
     error:
           sbus->cleanup();   
       /*activate the power down mode */
       u6_gpio_write_pin(GC2015_GPIO_PD,GC2015_GPIO_PD_ON);
 
       gc2015_unset_Mclk();
 
       ……
}

       这个函数调用了sensor-core.c中的i2c关联对象的初始化函数,然后调用set_devid()函数设置sensor的硬件地址。

static int i2c_init(void)
{
       interr = 0;
/* +LMSqc11271 'probe' is not working indevice driver. */
#ifdef CONFIG_I2C_NEW_PROBE
       structi2c_client *cam_i2c_client = NULL;
#endif
/* -LMSqc11271 'probe' is not working indevice driver. */
       PROLOG("");
 
       err = i2c_add_driver(&i2c_driver);   //注册一个i2c驱动
       if(err)
              CRITICAL("Failedto add Camera I2Cdriver");
/* +LMSqc11271 'probe' is not working indevice driver. */
#ifdef CONFIG_I2C_NEW_PROBE
       cam_i2c_client = camera_sbus_i2c.client;
#endif
/* -LMSqc11271 'probe' is not working indevice driver. */
      
       if(cam_i2c_client ==NULL) {
              i2c_del_driver(&i2c_driver);
              err= -ENODEV;
       }
 
       EPILOG("");
       returnerr;
}

       注册一个i2c驱动,这册过程伴随着设备与驱动的匹配过程,当匹配成功后就调用相应的probe()函数。

static int sensor_i2c_probe(struct i2c_client *new_client, const struct i2c_device_id *id)
{
       PROLOG("");
       i2c_set_clientdata(new_client,&camera_sbus_i2c);
       camera_sbus_i2c.client = new_client;
       EPILOG("");
       return0;
}

       至此,sensor设备已准备好,v4l2框架便可访问sensor设备了。

 

7  Cam驱动部分:

涉及的文件:Cam-core.c (src\linux\modules\cam)

Cam-lib.c (src\linux\modules\cam)

此驱动就是cam寄存器的函数封装,以及时钟信号的开关。本文件中的最重要数据cam_interface结构如下:

struct cam_interface cam = {
camPwr:NULL,
     camClk:NULL,
     jpegClk:NULL,
     IsInit:0,
     open_counter:0,
      whendone_cb:NULL,
      onerror_cb:NULL,
      data_cb:NULL,
     vdf:NULL,
     wait_encode:0,
     wait_header_generation:0,
     wait_data_transfert:0,
     wait_vsync:0,
     preview_hdl:0,
     zoom:1000,
     brightness:CAM_BRIGHT_DFT,
     saturation:CAM_SAT_DFT,
     contrast:CAM_CONTRAST_DFT,
     efx:NO_EFX,
     previewRunning:FALSE,
     immediateUpdate:FALSE,
}

       该模块的初始化函数:

intcam_init(void)
{
       PROLOG("");
 
       /* Reserve I/O addresses */
       if (!request_mem_region(CAM_IF_START,CAM_IF_SIZE, "CAM_IF")) {    
              CRITICAL("request_mem_regionfailed");
              return -EBUSY;
       }
 
       if (!cam.IsInit) {    /* do it one time */
 
              /* Get the CAM & CAMJPEGclocks */
              cam.camClk = clk_get(0, "CAM");
              if (IS_ERR(cam.camClk)) {
                     CRITICAL("Failed !(Could not get the CAM clock)");
                     return -ENXIO;
              }
 
              cam.jpegClk = clk_get(0,"CAMJPE");
              if (IS_ERR(cam.jpegClk)) {
                     CRITICAL("Failed !(Could not get the CAMJPEG clock)");
                     return -ENXIO;
              }
 
              /* Get the CAMpower */
              cam.camPwr = pwr_get(NULL, "CAM");
              if (IS_ERR(cam.camPwr)) {
                     CRITICAL("Failed !(Could not get the CAM power)");
                     return -ENXIO;
              }
              ……
       }
 
       EPILOG("");
       return 0;
}

该函数就是对cam成员进行初始化,如申请内存,获取时钟和电源等。下面是驱动中重要的函数:

 

(1)cam模块的启动函数

int cam_streaming_start(void *hdl,video_frame_t * frame, void *whendone_cb, void *onerror_cb, void *data_cb)

在这里面(前面有说过)上层v4l2.c中的callback函数就是通过cam_streaming_start函数,注册到底层cam驱动中,当一帧数据完成时候,中断函数会响应,从而callback函数也会被调用,唤醒comple等待的函数。

 

(2)切换到下一帧

void cam_update_stream_path(void *hdl,video_frame_t * frame)

 

(3)中断函数(cam_lib.c文件中定义)

irqreturn_t cam_int_irq(int irq, void *client_data)
{
  u32isp_itstat, mem_itstat;
 
  isp_itstat =cam_isp_isr_regs->cam_isp_mis;
  mem_itstat =cam_mem_isr_regs->cam_mem_mis;
  cam_jpe_regs->cam_jpe_status_mis;
 
……
   /***************** Memory interrupts ***********************/
else if (mem_itstat & CAM_IMSC_SP_FRAME_END) {
                PDEBUG("SP_FRAME_END,%lx", jiffies);
                cam_int_clear(0,CAM_IMSC_SP_FRAME_END, 0);
                /*call of whendonecallback */
                if(cam.whendone_cb != NULL) {
                       if(isp_itstat & CAM_IMSC_V_START) {
                              PTRACE("corruptedframe");
                       }else {
……
                              /*call user whendone */
                              cam.whendone_cb(cam.data_cb);
                       }
                }
         }
  }
 
……
}

所有cam模块内部的中断,都是此函数,红色标注部分为一帧数据到来,并最终调用到平台驱动的capture_callback()。

【上篇】
【下篇】

抱歉!评论已关闭.