1. 程式人生 > >Camera模組解析之驅動篇 .

Camera模組解析之驅動篇 .

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()。