【Camera專題】你應該熟悉的Camera驅動框架一(Hal層->kernel層)

c楓_擼碼的日子發表於2019-01-11

一、前言

本文主要研究展訊平臺 Camera驅動 和 HAL層程式碼架構,熟悉展訊Camera的控制流程。

Hal版本:【HAL3】

平臺:【Sprd展訊平臺】

知識點如下:

從HAL層到kernel層

1.Camera的開啟(open)、初始化(init)和供電(power on)呼叫流程

2.預覽(preview)呼叫流程

3.拍照(snapshot)呼叫流程

Camera軟體架構

camera軟體架構

二、Camera的開啟(open)、初始化(init)和供電(power on)呼叫流程

2.1 framework層的入口

經過App->framework層->jni層->cameraservice這個過程,接著呼叫到: frameworks/av/services/camera/libcameraservice/device3/Camera3Device.cpp

status_t Camera3Device::initialize(camera_module_t *module)
{
   ...
  /** Open HAL device */
    status_t res; 
    String8 deviceName = String8::format("%d", mId);

    camera3_device_t *device;
    //這裡呼叫modules->open函式開啟攝像頭
    res = module->common.methods->open(&module->common, deviceName.string(),
            reinterpret_cast<hw_device_t**>(&device));

    if (res != OK) {
        SET_ERR_L("Could not open camera: %s (%d)", strerror(-res), res);
        return res; 
    }    
...
}
複製程式碼

這裡呼叫module->common.methods->open開始操作HAL層,我們繼續往下看

2.2 HAL層

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3Factory.cpp

struct hw_module_methods_t SprdCamera3Factory::mModuleMethods = { 
    .open = SprdCamera3Factory::camera_device_open,
};
複製程式碼

實際上是呼叫的SprdCamera3Factory::camera_device_open方法。

int SprdCamera3Factory::camera_device_open(const struct hw_module_t *module,
                                           const char *id,
                                           struct hw_device_t **hw_device) {
···
    if (isSingleIdExposeOnMultiCameraMode(atoi(id))) {
        return gSprdCamera3Wrapper->cameraDeviceOpen(module, id, hw_device);
    } else {
        return gSprdCamera3Factory.cameraDeviceOpen(atoi(id), hw_device);
    }
···
}
複製程式碼

這裡open a camera device by its ID,通過ID來開啟攝像頭(後主攝:0 後副攝:2 前主攝:1 前副攝:3)

ID
接著繼續呼叫gSprdCamera3Factory.cameraDeviceOpen();

int SprdCamera3Factory::cameraDeviceOpen(int camera_id,
                                         struct hw_device_t **hw_device) {
···
    SprdCamera3HWI *hw =
        new SprdCamera3HWI(multiCameraModeIdToPhyId(camera_id));

    rc = hw->openCamera(hw_device);
···
    return rc;
}
複製程式碼

這裡new了一個SprdCamera3HWI的例項,然後呼叫openCamera(hw_device)方法。

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3HWI.cpp

int SprdCamera3HWI::openCamera(struct hw_device_t **hw_device) {
···
    ret = openCamera();

    if (ret == 0) { 
        *hw_device = &mCameraDevice.common;
        mCameraSessionActive++;
    } else 
        *hw_device = NULL;
···
    return ret; 
}
複製程式碼

接著繼續呼叫空構造方法openCamera();

int SprdCamera3HWI::openCamera() {
···
    //new SprdCamera3OEMIf的例項
    mOEMIf = new SprdCamera3OEMIf(mCameraId, mSetting);

    mOEMIf->camera_ioctrl(CAMERA_IOCTRL_SET_MULTI_CAMERAMODE, &mMultiCameraMode,
                          NULL);
    //開啟mOEMIf->openCamera()方法
    ret = mOEMIf->openCamera();

    mCameraOpened = true;
···
    if (mOEMIf->isIspToolMode()) {
        mOEMIf->ispToolModeInit();//初始化ispToolMode
        startispserver(mCameraId);
        ispvideo_RegCameraFunc(1, ispVideoStartPreview);//註冊ispVideoStartPreview函式
        ispvideo_RegCameraFunc(2, ispVideoStopPreview);//註冊ispVideoStopPreview 函式
        ispvideo_RegCameraFunc(3, ispVideoTakePicture);//註冊 ispVideoTakePicture函式
        ispvideo_RegCameraFunc(4, ispVideoSetParam);//註冊 ispVideoSetParam函式
    }
···
    return NO_ERROR;
}
複製程式碼

這裡new SprdCamera3OEMIf的例項,繼續呼叫mOEMIf->openCamera()方法。

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

int SprdCamera3OEMIf::openCamera() {
···
    //設定寬和高
    mSetting->getLargestPictureSize(mCameraId, &picW, &picH);
    mSetting->getLargestSensorSize(mCameraId, &snsW, &snsH);
    if (picW * picH > snsW * snsH) {
        mLargestPictureWidth = picW;
        mLargestPictureHeight = picH;
    } else {
        mLargestPictureWidth = snsW;
        mLargestPictureHeight = snsH;
    }
    //設定最大尺寸
    mHalOem->ops->camera_set_largest_picture_size(
        mCameraId, mLargestPictureWidth, mLargestPictureHeight);
   //呼叫startCameraIfNecessary繼續啟動攝像頭
    if (!startCameraIfNecessary()) {
        ret = UNKNOWN_ERROR;
        HAL_LOGE("start failed");
        goto exit;
    }
   //零延時模式執行緒初始化
    ZSLMode_monitor_thread_init((void *)this);

#ifdef CONFIG_CAMERA_GYRO
    gyro_monitor_thread_init((void *)this);
#endif

    property_get("persist.sys.camera.raw.mode", value, "jpeg");
    if (!strcmp(value, "raw") || !strcmp(value, "bin")) {
        is_raw_capture = 1;
    }

    property_get("persist.sys.isptool.mode.enable", value, "false");
    if (!strcmp(value, "true") || is_raw_capture) {
        mIsIspToolMode = 1;
    }
···
}
複製程式碼

SprdCamera3OEMIf::openCamera主要做了以下事情:

  1. 設定影象的最大尺寸
  2. 呼叫startCameraIfNecessary繼續啟動攝像頭
  3. 零延時模式執行緒初始化
  4. 根據persist.sys.camera.raw.mode和persist.sys.isptool.mode.enable設定屬性

繼續根據startCameraIfNecessary方法,這個方法做了很多事情,有些複雜。

bool SprdCamera3OEMIf::startCameraIfNecessary() {
···
  //如果camera沒有初始化,進行初始化
    if (!isCameraInit()) {
        HAL_LOGI("wait for camera_init");
        if (CMR_CAMERA_SUCCESS !=
            mHalOem->ops->camera_init(mCameraId, camera_cb, this, 0,
                                      &mCameraHandle, (void *)Callback_Malloc,
                                      (void *)Callback_Free)) {
            setCameraState(SPRD_INIT);
            HAL_LOGE("CameraIfNecessary: fail to camera_init().");
            return false;
        } else {
            setCameraState(SPRD_IDLE);
        }
···
      //獲得零延時快拍的相關引數
        mHalOem->ops->camera_get_zsl_capability(mCameraHandle, &is_support_zsl,
                                                &max_width, &max_height);
      //判斷是否支援零延時
        if (!is_support_zsl) {
            mParameters.setZSLSupport("false");
        }

        // 獲取抓取能力,包含3dnr能力
        mHalOem->ops->camera_ioctrl(
            mCameraHandle, CAMERA_IOCTRL_GET_GRAB_CAPABILITY, &grab_capability);

        /*從oem層獲取感測器和鏡頭資訊*/
        mHalOem->ops->camera_get_sensor_exif_info(mCameraHandle, &exif_info);
        mSetting->getLENSTag(&lensInfo);
        lensInfo.aperture = exif_info.aperture;
        mSetting->setLENSTag(lensInfo);

        /*從oem層獲取感測器otp*/

        /*開始讀取refoucs模式*/
        if (MODE_SINGLE_CAMERA != mMultiCameraMode &&
            MODE_3D_CAPTURE != mMultiCameraMode &&
            MODE_BLUR != mMultiCameraMode && MODE_BOKEH != mMultiCameraMode) {
            mSprdRefocusEnabled = true;
            CMR_LOGI("mSprdRefocusEnabled %d", mSprdRefocusEnabled);
        }
        /*結束讀取refoucs模式*/

        /*從oem層 獲取OPT資訊 開始 */
        if ((MODE_BOKEH == mMultiCameraMode || mSprdRefocusEnabled == true) &&
            mCameraId == 0) {
            OTP_Tag otpInfo;
            memset(&otpInfo, 0, sizeof(OTP_Tag));
            mSetting->getOTPTag(&otpInfo);
            ···
            struct sensor_otp_cust_info otp_info;
            memset(&otp_info, 0, sizeof(struct sensor_otp_cust_info));
            mHalOem->ops->camera_get_sensor_otp_info(mCameraHandle, &otp_info);
           ···
        }
        /*從oem 層獲取OTP資訊 結束*/
        /**新增3d校準,獲取最大感測器尺寸*/
        mSetting->getSPRDDEFTag(&sprddefInfo);
        mHalOem->ops->camera_get_sensor_info_for_raw(mCameraHandle, mode_info);
        for (i = SENSOR_MODE_PREVIEW_ONE; i < SENSOR_MODE_MAX; i++) {
            HAL_LOGD("trim w=%d, h=%d", mode_info[i].trim_width,
                     mode_info[i].trim_height);
            if (mode_info[i].trim_width * mode_info[i].trim_height >=
                sprddefInfo.sprd_3dcalibration_cap_size[0] *
                    sprddefInfo.sprd_3dcalibration_cap_size[1]) {
                sprddefInfo.sprd_3dcalibration_cap_size[0] =
                    mode_info[i].trim_width;
                sprddefInfo.sprd_3dcalibration_cap_size[1] =
                    mode_info[i].trim_height;
            }
        }
···
    return true;
}

複製程式碼

該函式已經在相應位置新增程式碼註釋,我們繼續關注呼叫流程,

通過mHalOem->ops->camera_init最終會呼叫到SprdOEMCamera.c程式碼的camera_init進行初始化

vendor/sprd/modules/libcamera/oem2v1/src/SprdOEMCamera.c

cmr_int camera_init(cmr_u32 camera_id, camera_cb_of_type callback,
                    void *client_data, cmr_uint is_autotest,
                    cmr_handle *camera_handle, void *cb_of_malloc,
                    void *cb_of_free) {
···
    //初始化OEM的log   
    oem_init_log_level();

    //呼叫camera_local_int繼續進行初始化
    ret = camera_local_int(camera_id, callback, client_data, is_autotest,
                           camera_handle, cb_of_malloc, cb_of_free); 
···
    //其他的一些初始化
    camera_lls_enable(*camera_handle, 0);
    camera_set_lls_shot_mode(*camera_handle, 0);
    camera_vendor_hdr_enable(*camera_handle, 0);
···
    return ret; 
}
複製程式碼

vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_int(cmr_u32 camera_id, camera_cb_of_type callback,
                         void *client_data, cmr_uint is_autotest,
                         cmr_handle *oem_handle, void *cb_of_malloc,
                         void *cb_of_free) {
···
    //記憶體申請
    struct camera_context *cxt = NULL;
    *oem_handle = (cmr_handle)0;
    cxt = (struct camera_context *)malloc(sizeof(struct camera_context));

    //引數賦值
    cmr_bzero(cxt, sizeof(*cxt));
    cxt->camera_id = camera_id;
    cxt->camera_cb = callback;
    cxt->client_data = client_data;
    cxt->hal_malloc = cb_of_malloc;
    cxt->hal_free = cb_of_free;
    cxt->hal_gpu_malloc = NULL; 
    cxt->is_multi_mode = is_multi_camera_mode_oem;
    cxt->blur_facebeauty_flag = 0;
    
    //呼叫camera_init_internal進行下一步初始化
    ret = camera_init_internal((cmr_handle)cxt, is_autotest);
···
    return ret;
}
複製程式碼

呼叫camera_init_internal進行下一步初始化

cmr_int camera_init_internal(cmr_handle oem_handle, cmr_uint is_autotest) {
···
    //sensor初始化
    ret = camera_sensor_init(oem_handle, is_autotest);
    if (ret) {
        CMR_LOGE("failed to init sensor %ld", ret);
        goto exit;
    }
    //grab初始化
    ret = camera_grab_init(oem_handle);
    if (ret) {
        CMR_LOGE("failed to init grab %ld", ret);
        goto sensor_deinit;
    }
    //res初始化
    ret = camera_res_init(oem_handle);
    if (ret) {
        CMR_LOGE("failed to init res %ld", ret);
        goto grab_deinit;
    }
    //isp初始化
    ret = camera_isp_init(oem_handle);
    if (ret) {
        CMR_LOGE("failed to init isp %ld", ret);
        goto res_deinit;
    }
    //初始化完成
    ret = camera_res_init_done(oem_handle);

···
    return ret;
}
複製程式碼

該函式主要做了以下事情:

  1. sensor初始化
  2. grab初始化
  3. res初始化
  4. isp初始化 我們繼續關注camera_sensor_init這個函式
cmr_int camera_sensor_init(cmr_handle oem_handle, cmr_uint is_autotest) {
···
    ret = cmr_sensor_init(&init_param, &sensor_handle);

    ret = cmr_sensor_open(sensor_handle, camera_id_bits);
···
}
複製程式碼

該函式分別呼叫了cmr_sensor_init初始化和cmr_sensor_open開啟Camera

vendor/sprd/modules/libcamera/oem2v1/src/cmr_sensor.c

cmr_int cmr_sensor_init(struct sensor_init_param *init_param_ptr,
                        cmr_handle *sensor_handle) {
···
    /*save init param*/
    handle->oem_handle = init_param_ptr->oem_handle;
    handle->sensor_bits = init_param_ptr->sensor_bits;
    handle->private_data = init_param_ptr->private_data;
    handle->is_autotest = init_param_ptr->is_autotest;

    /*create thread*/
    ret = cmr_sns_create_thread(handle);
···
    return ret;
}
複製程式碼

該函式 對一些引數進行賦值,然後呼叫cmr_sns_create_thread方法建立cmr_sns_thread_proc執行緒。 如下: ret = cmr_thread_create(&handle->thread_cxt.thread_handle, SENSOR_MSG_QUEUE_SIZE,cmr_sns_thread_proc, (void *)handle)

cmr_int cmr_sensor_open(cmr_handle sensor_handle, cmr_u32 sensor_id_bits) {
···
    struct cmr_sensor_handle *handle =
        (struct cmr_sensor_handle *)sensor_handle;

    /*the open&close function should be sync*/
    message.msg_type = CMR_SENSOR_EVT_OPEN;
    message.sync_flag = CMR_MSG_SYNC_PROCESSED;
    message.data = (void *)((unsigned long)sensor_id_bits);
    //這裡傳送msg訊息,去啟動在cmr_sensor_init建立的
    ret = cmr_thread_msg_send(handle->thread_cxt.thread_handle, &message);
···
    return ret;
}
複製程式碼

在cmr_sensor_open中,傳送了msg訊息,去啟動在cmr_sensor_init建立的執行緒cmr_sns_thread_proc。

這裡的訊息型別是message.msg_type = CMR_SENSOR_EVT_OPEN;

cmr_int cmr_sns_thread_proc(struct cmr_msg *message, void *p_data) {
···
    switch (evt) {
    case CMR_SENSOR_EVT_INIT:
        /*common control info config*/
        CMR_LOGI("INIT DONE!");
        break;

    case CMR_SENSOR_EVT_OPEN:
        /*camera sensor open for every bits*/
        ops_param = (cmr_u32)((unsigned long)message->data);
        ret = cmr_sns_open(handle, ops_param);
        if (ret) {
            /* notify oem through fd_sensor */
            CMR_LOGE("cmr_sns_open failed!");
        }    
        return CMR_CAMERA_INVALID_PARAM;
    }
 ···
}
複製程式碼

因此,接下來會走case CMR_SENSOR_EVT_OPEN這個分支,呼叫cmr_sns_open方法。

cmr_int cmr_sns_open(struct cmr_sensor_handle *handle, cmr_u32 sensor_id_bits) {
···
    /*open all signed camera sensor*/
    for (cameraId = 0; cameraId < CAMERA_ID_MAX; cameraId++) {
        if (0 != (sensor_id_bits & (1 << cameraId))) {
            ret = sensor_open_common(&handle->sensor_cxt[cameraId], cameraId,
                                     handle->is_autotest);
            if (ret) {
                CMR_LOGE("camera %u open failed!", cameraId);
            } else {
                handle->sensor_bits |= (1 << cameraId);
            }
        }
    }
···
}
複製程式碼

cmr_sns_open方法又繼續呼叫sensor_open_common函式,這個函式比較複雜,主要工作如下:

  1. 初始化ctx(context)這個結構體
  2. 初始化exif資訊(拍照資訊)
  3. 載入sensor file檔案,裡面儲存了camera的id
  4. 根據sensor file裡儲存的camera 的id開啟攝像頭 我們來看程式碼: vendor/sprd/modules/libcamera/sensor/sensor_drv_u.c
cmr_int sensor_open_common(struct sensor_drv_context *sensor_cxt,
                           cmr_u32 sensor_id, cmr_uint is_autotest) {
···
    /* 呼叫sensor_context_init 初始化ctx(context)這個結構體*/
    ret_val = sensor_context_init(sensor_cxt, sensor_id, is_autotest);
  
    /* 建立sensor_ctrl_thread_proc執行緒. */
    ret_val = sensor_create_ctrl_thread(sensor_cxt);

    /* 初始化核心驅動程式的結構體hw_drv_init_para . */
    struct hw_drv_init_para input_ptr;
    cmr_int fd_sensor = SENSOR_FD_INIT;//SENSOR_FD_INIT =-1
    cmr_handle hw_drv_handle = NULL;
    input_ptr.sensor_id = sensor_id;
    input_ptr.caller_handle = sensor_cxt;
    fd_sensor = hw_sensor_drv_create(&input_ptr, &hw_drv_handle);
    if ( (SENSOR_FD_INIT == fd_sensor) || (NULL == hw_drv_handle) ) {
        SENSOR_LOGE("sns_device_init %d error, return", sensor_id);
        ret_val = SENSOR_FAIL;
        goto init_exit;
    }
    //初始化sensor_cxt
    sensor_cxt->fd_sensor = fd_sensor;
    sensor_cxt->hw_drv_handle = hw_drv_handle;
    sensor_cxt->sensor_hw_handler = hw_drv_handle;

   /* 根據儲存在感測器idx檔案中的索引載入所有感測器ic資訊*/
    sensor_load_idx_inf_file(sensor_cxt);
    if (sensor_cxt->sensor_identified) {
        if (SENSOR_SUCCESS == sns_load_drv(sensor_cxt, SENSOR_MAIN)){
            sensor_num++;
        }
···
        SENSOR_LOGI("1 is identify, register OK");
        /*讀到id資訊,就去open*/
        ret_val = sensor_open(sensor_cxt, sensor_id);
        if (ret_val != SENSOR_SUCCESS) {
            SENSOR_LOGI("first open sensor failed,start identify");
        }
    }

    /* 掃描cfg表中的裝置,找出正確的感測器驅動程式 */
    if ((!sensor_cxt->sensor_identified) || (ret_val != SENSOR_SUCCESS)) {
        sensor_num = 0;
        SENSOR_LOGI("register sensor fail, start identify");
        //遍歷的核心函式是sensor_identify
        if (sensor_identify(sensor_cxt, SENSOR_MAIN))
            sensor_num++;
···
        //遍歷成功後,繼續執行sensor_open動作
        ret_val = sensor_open(sensor_cxt, sensor_id);
    }
    sensor_cxt->sensor_identified = SCI_TRUE;//設定sensor_id的狀態為TRUE

    sensor_save_idx_inf_file(sensor_cxt);//把識別到的id資訊儲存到/data/misc/cameraserver/sensor.file
    //把節點資訊儲存到/sys/devices/virtual/misc/sprd_sensor/camera_sensor_name
    sensor_rid_save_sensor_info(sensor_cxt);
···
    return ret_val;
}
複製程式碼

分析:首先進行一些必要的初始化,然後呼叫sensor_load_idx_inf_file函式去載入**/data/misc/cameraserver/路徑下的sensor.file**檔案,

  1. 如果讀到了sensor_id, sensor_cxt->sensor_identified 設定為SCI_TRUE(這個值是1),走sns_load_drv(sensor_cxt, SENSOR_MAIN)函式去註冊驅動程式,接著直接執行sensor_open動作。

  2. 否則,呼叫sensor_identify(sensor_cxt, SENSOR_MAIN)遍歷sensor list,掃描cfg表中的裝置,找出正確的感測器驅動程式。

以上成功後,呼叫sensor_open函式進行:

  1. AF的初始化 sensor_af_init()
  2. OTP的讀取 otp_module_init()
  3. 拍照資訊的設定 sensor_set_export_Info()

流程圖如下:

sensor_open_common流程

sensor_identify掃描流程

LOCAL cmr_int sensor_identify(struct sensor_drv_context *sensor_cxt,
                              SENSOR_ID_E sensor_id) {
···
    ret = sensor_get_match_info(sensor_cxt, sensor_id);

    ret = sensor_ic_identify(sensor_cxt, sensor_id);

    retValue = sensor_identify_search(sensor_cxt, sensor_id);
    return retValue;
}
複製程式碼

分析: #1.首先呼叫sensor_get_match_info去獲取我們自己配置的camera驅動,流程是:

sensor_get_match_info -> sensor_get_module_tab -> back_sensor_infor_tab(如下所示)

vendor/sprd/modules/libcamera/sensor/sensor_cfg.c

這就是為啥我們驅動工程師新增新的Camea時,都要在這個cfg列表裡新增我們的驅動

const SENSOR_MATCH_T back_sensor_infor_tab[] = {
// gc area
#ifdef GC5005
    {MODULE_SUNNY, "gc5005", &g_gc5005_mipi_raw_info, {&dw9714_drv_entry, 0}, NULL},
#endif
#ifdef GC8024
    {MODULE_SUNNY, "gc8024", &g_gc8024_mipi_raw_info, {&dw9714_drv_entry, 0}, NULL},
#endif
#ifdef GC030A
    {MODULE_SUNNY, "gc030a", &g_gc030a_mipi_raw_info, {NULL, 0}, NULL},
#endif
#ifdef GC2385
    {MODULE_SUNNY, "gc2385", &g_gc2385_mipi_raw_info, {NULL, 0}, NULL},
#endif
···
}
複製程式碼

#2.然後呼叫sensor_ic_identify去識別ic資訊。 識別步驟如下: 1. 建立sensor IC驅動結構體

2.配置I2C匯流排,感測器ID, I2C時鐘,從addr, reg addr lenth,資料長度

3.給sensor IC 上電

4.識別sensor IC 的PID和VID

5.刪除sensor IC驅動結構體

sensor_ic_identify遍歷的流程圖如下,

通過 sns_ops->power(sensor_cxt->sns_ic_drv_handle, power_on);呼叫到sensor驅動的power_on介面, 如ov8856_drv_power_on()

sns_ops->identify(sensor_cxt->sns_ic_drv_handle,SENSOR_ZERO_I2C);呼叫到sensor驅動的identify介面, 如ov8856_drv_identify()

identify流程

static cmr_int sensor_ic_identify(struct sensor_drv_context *sensor_cxt,
                                  cmr_u32 sensor_id) {

···
    //1.建立sensor IC驅動結構體
    struct sensor_ic_ops *sns_ops = PNULL;
    struct sensor_ic_drv_init_para sns_init_para;

    register_info = &sensor_cxt->sensor_register_info;
    sns_ops = sensor_cxt->sensor_info_ptr->sns_ops;

    sensor_cxt->i2c_addr = mod_cfg_info->major_i2c_addr;
    /* 建立 sensor ic handle */
    ret = sensor_ic_create(sensor_cxt, sensor_id);

try: /*sensor has backup addr*/
        if (sns_ops && sns_ops->identify) {

            /*2.初始化 i2c配置*/
            hw_drv_cfg.i2c_bus_config = mod_cfg_info->reg_addr_value_bits;
            hw_sensor_drv_cfg(sensor_cxt->hw_drv_handle, &hw_drv_cfg);
            sensor_i2c_init(sensor_cxt, sensor_id);
            //設定i2c地址
            hw_sensor_i2c_set_addr(sensor_cxt->hw_drv_handle,
                                   sensor_cxt->i2c_addr);
            //設定i2c時鐘
            hw_sensor_i2c_set_clk(sensor_cxt->hw_drv_handle);
···
            //3.給sensor 上電
            sensor_power_on(sensor_cxt, SCI_TRUE); /*power on*/
            //呼叫具體的驅動進行identify
            ret = sns_ops->identify(sensor_cxt->sns_ic_drv_handle,
                                    SENSOR_ZERO_I2C);
            if (SENSOR_SUCCESS == ret) {
                /**if the following is SCI_FALSE,that is,now is in identify
                 *process
                 * should delete sensor ic handle
                 **/
                if (register_info->is_register[sensor_id] != SCI_TRUE) {
                    sensor_power_on(sensor_cxt, SCI_FALSE);
                    sensor_i2c_deinit(sensor_cxt, sensor_id);
                    sensor_ic_delete(sensor_cxt);
                }
                sensor_cxt->sensor_list_ptr[sensor_id] =
                    sensor_cxt->sensor_info_ptr;
                register_info->is_register[sensor_id] = SCI_TRUE;
                register_info->img_sensor_num++;
            } else {
                // register_info->is_register[sensor_id] = SCI_FALSE;
                sensor_power_on(sensor_cxt, SCI_FALSE);
                if ((sensor_cxt->i2c_addr != mod_cfg_info->minor_i2c_addr) &&
                    mod_cfg_info->minor_i2c_addr != 0x00) {
                    sensor_cxt->i2c_addr = mod_cfg_info->minor_i2c_addr;
                    SENSOR_LOGI("use backup i2c address,try again!");
                    goto try
                        ;
                }
                SENSOR_LOGI("identify failed!");
                //如果identify failed就刪除sensor IC資訊
                sensor_ic_delete(sensor_cxt);
                return SENSOR_FAIL;
            }
        }

    return ret;
}
複製程式碼

PS: power on 流程 也是我們驅動工程師經常修改的地方,這裡囉嗦幾句,以ov8856的上電為例子

這的主要是三路電壓,avdd,dvdd,iovdd設定供電,具體參考我之前寫的文章: 你應該瞭解的Camera HW-硬體知識

1.供電部分 camera包含的三路電壓為模擬電壓(VCAMA),數字電壓(VCAMD),IO口電壓(VCAMIO)

a) VCAMD 就是 DVDD 數字供電,主要給 ISP 供電

b) VCAM_IO 就是 VDDIO 數字 IO 電源主要給 I2C 部分供電;

c) VCAMA 就是 AVDD 模擬供電,主要給感光區和 ADC 部分供電;

d) VCAM_AF 是對 Camera 自動對焦馬達的供電

/*==============================================================================
 * Description:
 * sensor power on
 * please modify this function acording your spec
 *============================================================================*/
static cmr_int ov8856_drv_power_on(cmr_handle handle, cmr_u32 power_on) {
    SENSOR_IC_CHECK_HANDLE(handle);
···
    if (SENSOR_TRUE == power_on) {
        //上電流程
        //先拉低pnd腳
        hw_sensor_power_down(sns_drv_cxt->hw_handle, power_down);
        //拉低reset腳
        hw_sensor_set_reset_level(sns_drv_cxt->hw_handle, reset_level);
        usleep(500);//延遲500微秒,ps這裡的延遲要根據規格書來
        //設定av電壓,主要給感官區和adc部分供電
        hw_sensor_set_avdd_val(sns_drv_cxt->hw_handle, avdd_val);
        //設定DVDD 電壓,主要給ISP供電
        hw_sensor_set_dvdd_val(sns_drv_cxt->hw_handle, dvdd_val);
        //設定IO電壓,IO 電源主要給 I2C 部分供電
        hw_sensor_set_iovdd_val(sns_drv_cxt->hw_handle, iovdd_val);
        usleep(500);//延遲500微秒
        //拉高PND腳
        hw_sensor_power_down(sns_drv_cxt->hw_handle, !power_down);
        //拉高rst腳
        hw_sensor_set_reset_level(sns_drv_cxt->hw_handle, !reset_level);
        usleep(500);//延遲500微秒
        //設定mclk時鐘
        hw_sensor_set_mclk(sns_drv_cxt->hw_handle, EX_MCLK);
    } else {//下電流程,和上電相反
        hw_sensor_set_mclk(sns_drv_cxt->hw_handle, SENSOR_DISABLE_MCLK);
        usleep(500);
        hw_sensor_set_reset_level(sns_drv_cxt->hw_handle, reset_level);
        hw_sensor_power_down(sns_drv_cxt->hw_handle, power_down);
        usleep(200);
        hw_sensor_set_avdd_val(sns_drv_cxt->hw_handle, SENSOR_AVDD_CLOSED);
        hw_sensor_set_dvdd_val(sns_drv_cxt->hw_handle, SENSOR_AVDD_CLOSED);
        hw_sensor_set_iovdd_val(sns_drv_cxt->hw_handle, SENSOR_AVDD_CLOSED);
    }
    SENSOR_LOGI("(1:on, 0:off): %d", power_on);
    return SENSOR_SUCCESS;
}
複製程式碼

PS2:identify的實現也貼出來,繼續囉嗦幾句,以ov8856為例子 新增了關鍵程式碼註釋,很容易理解!

/*==============================================================================
 * Description:
 * identify sensor id
 * please modify this function acording your spec
 *============================================================================*/
static cmr_int ov8856_drv_identify(cmr_handle handle, cmr_uint param) {
···
    //hw_sensor_read_reg 讀取暫存器資訊
    pid_value = hw_sensor_read_reg(sns_drv_cxt->hw_handle, ov8856_PID_ADDR);
    //識別到具體的sendor id
    if (ov8856_PID_VALUE == pid_value) {
        ver_value = hw_sensor_read_reg(sns_drv_cxt->hw_handle, ov8856_VER_ADDR);
        SENSOR_LOGI("Identify: PID = %x, VER = %x", pid_value, ver_value);
        if (ov8856_VER_VALUE == ver_value) {
            SENSOR_LOGI("this is ov8856 sensor");
            //把id資訊儲存起來
            ov8856_drv_init_fps_info(handle);
            ret_value = SENSOR_SUCCESS;
        } else {
            SENSOR_LOGI("Identify this is %x%x sensor", pid_value, ver_value);
        }
    } else {
        SENSOR_LOGE("sensor identify fail, pid_value = %x", pid_value);
    }

    return ret_value;
}
複製程式碼

#3.最後如果identify失敗,則重新執行上面2個步驟,重新遍歷

【Camera專題】你應該熟悉的Camera驅動框架一(Hal層->kernel層)
sensor_identify_search函式實現如下:

LOCAL cmr_u32 sensor_identify_search(struct sensor_drv_context *sensor_cxt,
                                     SENSOR_ID_E sensor_id) {
···
    //呼叫sensor_get_match_info去獲取我們自己配置的camera驅動
    module_tab = sensor_get_module_tab(sensor_cxt->is_autotest, sensor_id);
···
    //呼叫sensor_ic_identify去識別ic資訊
    retValue = sensor_ic_identify(sensor_cxt, sensor_id);
···
    return retValue;
}
複製程式碼

到此文章的第一部分就寫完了,鬆口氣,喝口水,繼續寫第二部分內容!

三、預覽(preview)呼叫流程

【Hal層】

vendor/sprd/modules/libcamera/hal3_2v1a/SprdCamera3HWI.cpp

int SprdCamera3HWI::openCamera() {
···
      //註冊ispVideoStartPreview函式
       ispvideo_RegCameraFunc(1, ispVideoStartPreview);
···
}
複製程式碼

在openCamera函式中,通過這個ispvideo_RegCameraFunc(1, ispVideoStartPreview);註冊ispVideoStartPreview

static int ispVideoStartPreview(uint32_t param1, uint32_t param2) {
···
rtn = regularChannel->start(dev->mFrameNum);
···
}
複製程式碼

接下來呼叫regularChannel->start(dev->mFrameNum)往下走

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3Channel.cpp

int SprdCamera3RegularChannel::start(uint32_t frame_number) {
    int ret = NO_ERROR;
    size_t i = 0; 

    ret = mOEMIf->start(mChannelType, frame_number);
    return ret; 
}
複製程式碼

這裡的type :

typedef enum {
CAMERA_CHANNEL_TYPE_DEFAULT, / default /
CAMERA_CHANNEL_TYPE_REGULAR, / regular channel /
CAMERA_CHANNEL_TYPE_PICTURE, / picture channel/
CAMERA_CHANNEL_TYPE_RAW_CALLBACK, /YUV888 callback/
CAMERA_CHANNEL_TYPE_MAX,
} camera_channel_type_t;
複製程式碼

接著呼叫 ret = mOEMIf->start(mChannelType, frame_number);

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

int SprdCamera3OEMIf::start(camera_channel_type_t channel_type,
                            uint32_t frame_number) {
···
    switch (channel_type) {
    case CAMERA_CHANNEL_TYPE_REGULAR: {
···
        ret = startPreviewInternal();//這裡繼續跟進去
        break;
    }
    //以下是拍照部分,我們下個部分在進行分析
    case CAMERA_CHANNEL_TYPE_PICTURE: {
        if (mTakePictureMode == SNAPSHOT_NO_ZSL_MODE ||
                ret = takePicture();
            }
        else if (mTakePictureMode == SNAPSHOT_ZSL_MODE) {

                ret = zslTakePicture();
        } else if (mTakePictureMode == SNAPSHOT_VIDEO_MODE) {
            ret = VideoTakePicture();
        }
        break;
    }
    default:
        break;
    }
···
    return ret;
}
複製程式碼

如果型別為CAMERA_CHANNEL_TYPE_REGULAR,則呼叫:

ret = startPreviewInternal();//這裡繼續跟進去

如果型別為CAMERA_CHANNEL_TYPE_PICTURE,則呼叫拍照相關:

ret = takePicture();

ret = zslTakePicture();

ret = VideoTakePicture();

int SprdCamera3OEMIf::startPreviewInternal() {
···
    //preview的時候,設定照片的thumbnail size(壓縮後的大小)和camera app的大小一致
    chooseDefaultThumbnailSize(&jpeg_thumb_size.width, &jpeg_thumb_size.height);
···
    ret = mHalOem->ops->camera_start_preview(mCameraHandle, mCaptureMode);
···
}
複製程式碼

mHalOem->ops->camera_start_preview(mCameraHandle, mCaptureMode);方法的實現在SprdOEMCamera.c裡 vendor/sprd/modules/libcamera/oem2v1/src/SprdOEMCamera.c

cmr_int camera_start_preview(cmr_handle camera_handle,
                             enum takepicture_mode mode) {
···
    ret = camera_local_start_preview(camera_handle, mode, CAMERA_PREVIEW);
···
    return ret; 
}
複製程式碼

【OEM層】

該函式很簡單,就繼續呼叫camera_local_start_preview函式 vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_start_preview(cmr_handle oem_handle,
                                   enum takepicture_mode mode, 
                                   cmr_uint is_snapshot) {
    //設定preview的引數
    ret = camera_set_preview_param(oem_handle, mode, is_snapshot);
    //繼續cmr_preview_start
    ret = cmr_preview_start(prev_cxt->preview_handle, cxt->camera_id);

···
    return ret;
}
複製程式碼

該函式設定preview的引數資訊,然後繼續呼叫cmr_preview_start方法 vendor/sprd/modules/libcamera/oem2v1/src/cmr_preview.c

cmr_int cmr_preview_start(cmr_handle preview_handle, cmr_u32 camera_id) {
···
    message.msg_type = PREV_EVT_ASSIST_START;//設定msg的type型別PREV_EVT_ASSIST_START
    message.sync_flag = CMR_MSG_SYNC_PROCESSED;//設定msg的flag
    //傳送msg訊息
    ret = cmr_thread_msg_send(handle->thread_cxt.assist_thread_handle, &message);
···
    message.msg_type = PREV_EVT_START;//設定msg的type型別PREV_EVT_START
    message.sync_flag = CMR_MSG_SYNC_PROCESSED;//設定msg的flag
    message.data = (void *)((unsigned long)camera_id);
    //傳送了msg訊息
    ret = cmr_thread_msg_send(handle->thread_cxt.thread_handle, &message);

···
    return ret;
}
複製程式碼

這裡主要呼叫cmr_thread_msg_send傳送兩條msg訊息,

第一條msg=訊息

assist_thread_handle=prev_assist_thread_proc 該handle的建立: 在prev_create_thread(struct prev_handle *handle)呼叫 ret = cmr_thread_create(&handle>thread_cxt.assist_thread_handle,PREV_MSG_QUEUE_SIZE, prev_assist_thread_proc, (void *)handle);

cmr_int prev_assist_thread_proc(struct cmr_msg *message, void *p_data) {
···
    msg_type = (cmr_u32)message->msg_type;//獲得msg_tyoe
    //根據msg_type進行操作
    switch (msg_type) {
    case PREV_EVT_ASSIST_START:
        handle->frame_active = 1;
        break;
···
    case PREV_EVT_ASSIST_STOP:
        handle->frame_active = 0;
        break;
···
    return ret;
}
複製程式碼

當msg_type=PREV_EVT_ASSIST_START: 僅僅操作handle->frame_active = 1;

第二條msg=訊息

thread_cxt.thread_handle=prev_thread_proc

cmr_int prev_thread_proc(struct cmr_msg *message, void *p_data) {
···
    switch (msg_type) {
···
    case PREV_EVT_START:
        camera_id = (cmr_u32)((unsigned long)message->data);

        prev_recovery_reset(handle, camera_id);
        ret = prev_start(handle, camera_id, 0, 0);
        /*Notify preview started*/
        cb_data_info.cb_type = PREVIEW_EXIT_CB_PREPARE;
        cb_data_info.func_type = PREVIEW_FUNC_START_PREVIEW;
        cb_data_info.frame_data = NULL;
        prev_cb_start(handle, &cb_data_info);
        break;
···
}
複製程式碼

分析: 1.ret = prev_start(handle, camera_id, 0, 0)呼叫流程如下:

ret = handle->ops.channel_start(···);【cmr_preview.c】
  ->cmr_int camera_channel_start(···);【cmr_oem.c】
    ->cmr_int cmr_grab_cap_start(···)【cmr_grab.c】
      ->ret = ioctl(p_grab->fd, SPRD_IMG_IO_SET_CAP_SKIP_NUM, &num);【cmr_grab.c】
複製程式碼

【kernel層】

通過ioctl的方式呼叫kernel層的方法 經過以上一系列複雜流程,後看到cmr_grab_cap_start()調入到kernel目錄執行開啟DCAM,

cmr_grab_cap_start通過ioctl的方式呼叫kernel層的方法。

cmr_int cmr_grab_cap_start(cmr_handle grab_handle, cmr_u32 skip_num) {
···
    ret = ioctl(p_grab->fd, SPRD_IMG_IO_SET_CAP_SKIP_NUM, &num);
    ATRACE_BEGIN("dcam_stream_on");
    ret = ioctl(p_grab->fd, SPRD_IMG_IO_STREAM_ON, &stream_on);
···
    return ret;
}
複製程式碼

kernel/drivers/misc/sprd_camera/dcam/dcam_if_r4p0/dcam_ioctrl.c

{SPRD_IMG_IO_STREAM_ON,		dcamio_stream_on},
複製程式碼
static int dcamio_stream_on(struct camera_file *camerafile,
             unsigned long arg, 
             unsigned int cmd) 
{
···
    ret = sprd_img_get_dcam_dev(camerafile, &dev, &info);

    ret = sprd_camera_stream_on(camerafile);
···
    return ret; 
}
複製程式碼

2.prev_cb_start(handle, &cb_data_info)呼叫流程如下:

prev_cb_start(handle, &cb_data_info)//cmr_preview.c
 ->ret = cmr_thread_msg_send(···);//cmr_preview.c
   //message.msg_type = PREV_EVT_CB_START;cb_thread_handle = prev_cb_thread_proc
  ->ret = handle->oem_cb(···)//cmr_preview.c
      //handle->oem_cb = init_param_ptr->oem_cb=camera_preview_cb;
    ->ret = cmr_thread_msg_send(···);// oem2v1/src/cmr_oem.c
      //message.sub_msg_type = oem_cb_type;
      //prev_cb_thr_handle = camera_preview_cb_thread_proc
      ->callback(···);
複製程式碼

vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_preview_cb_thread_proc(struct cmr_msg *message, void *data) {
···
  callback = cxt->camera_cb;
  callback(message->sub_msg_type, cxt->client_data, 
                        message->msg_type, message->data);
···
    return ret; 
}
複製程式碼

這裡callback 為 cxt->camera_cb;具體實現在SprdCamera3OEMIf::camera_cb(···);

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

void SprdCamera3OEMIf::camera_cb(enum camera_cb_type cb,
                                 const void *client_data,
                                 enum camera_func_type func, void *parm4) {
···
    switch (func) {
    case CAMERA_FUNC_START_PREVIEW:
        obj->HandleStartPreview(cb, parm4);
        break;
···
複製程式碼

這裡在oem_func = CAMERA_FUNC_START_PREVIEW;因此繼續呼叫HandleStartPreview(cb, parm4);

void SprdCamera3OEMIf::HandleStartPreview(enum camera_cb_type cb, void *parm4) {
···
            receivePreviewFrame((struct camera_frame_type *)parm4);
···
}
複製程式碼

這裡是呼叫receivePreviewFrame接收frame data

void SprdCamera3OEMIf::receivePreviewFrame(struct camera_frame_type *frame) {
···
    //接收frame data
    channel->getStream(CAMERA_STREAM_TYPE_PREVIEW, &pre_stream);
    channel->getStream(CAMERA_STREAM_TYPE_VIDEO, &rec_stream);
    channel->getStream(CAMERA_STREAM_TYPE_CALLBACK, &callback_stream);
    HAL_LOGV("pre_stream %p, rec_stream %p, callback_stream %p", pre_stream,
             rec_stream, callback_stream);
//美顏
#ifdef CONFIG_FACE_BEAUTY
    int sx, sy, ex, ey, angle, pose;
    struct face_beauty_levels beautyLevels;
    beautyLevels.blemishLevel =
        (unsigned char)sprddefInfo.perfect_skin_level[0];
    beautyLevels.smoothLevel = (unsigned char)sprddefInfo.perfect_skin_level[1];
    beautyLevels.skinColor = (unsigned char)sprddefInfo.perfect_skin_level[2];
    beautyLevels.skinLevel = (unsigned char)sprddefInfo.perfect_skin_level[3];
    beautyLevels.brightLevel = (unsigned char)sprddefInfo.perfect_skin_level[4];
    beautyLevels.lipColor = (unsigned char)sprddefInfo.perfect_skin_level[5];
    beautyLevels.lipLevel = (unsigned char)sprddefInfo.perfect_skin_level[6];
    beautyLevels.slimLevel = (unsigned char)sprddefInfo.perfect_skin_level[7];
    beautyLevels.largeLevel = (unsigned char)sprddefInfo.perfect_skin_level[8];
#endif
···
}
複製程式碼

這個函式實現很複雜,主要用來recevie Frame data here , 以及美顏等,具體細節以後分析。

四、拍照(snapshot)呼叫流程

【Hal層】

我們直接從SprdCamera3OEMIf::start開始分析,怎麼呼叫到這個函式的,前面已經分析過了,就不在贅述!

vendor/sprd/modules/libcamera/hal3_2v1/SprdCamera3OEMIf.cpp

int SprdCamera3OEMIf::start(camera_channel_type_t channel_type,
                            uint32_t frame_number) {
···
    switch (channel_type) {
···
    case CAMERA_CHANNEL_TYPE_PICTURE: {
        if (···)
            setCamPreformaceScene(CAM_CAPTURE_S_LEVEL_NH);
        }

        if (mTakePictureMode == SNAPSHOT_NO_ZSL_MODE ||
            mTakePictureMode == SNAPSHOT_ONLY_MODE)
            ···
                ret = takePicture();
            ···
        else if (mTakePictureMode == SNAPSHOT_ZSL_MODE) {
            mVideoSnapshotFrameNum = frame_number;
            ···
                ret = zslTakePicture();
            ···
        } else if (mTakePictureMode == SNAPSHOT_VIDEO_MODE) {
            mVideoSnapshotFrameNum = frame_number;
            ret = VideoTakePicture();
        }
        break;
    }
···
    }
···
}
複製程式碼

分析:首先channel_type=CAMERA_CHANNEL_TYPE_PICTURE,然後進行以下動作: 1.setCamPreformaceScene(CAM_CAPTURE_S_LEVEL_NH);設定Camera的場景,場景型別如下

typedef enum CAMERA_PERFORMACE_SCENE {
    CAM_OPEN_S,
    CAM_OPEN_E_LEVEL_H,     // DFS:veryhigh
    CAM_OPEN_E_LEVEL_N,     // DFS:normal
    CAM_OPEN_E_LEVEL_L,     // DFS:low
    CAM_PREVIEW_S_LEVEL_H,  // powerhint:performance
    CAM_PREVIEW_S_LEVEL_N,  // powerhint:normal
    CAM_PREVIEW_S_LEVEL_L,  // powerhint:low
    CAM_CAPTURE_S_LEVEL_HH, // powerhint:performance  DFS:veryhigh
    CAM_CAPTURE_S_LEVEL_HN, // powerhint:performance  DFS:normal
    CAM_CAPTURE_S_LEVEL_NH, // powerhint:normal  DFS:veryhigh
    CAM_CAPTURE_S_LEVEL_NN, // powerhint:normal  DFS:normal
    CAM_CAPTURE_E_LEVEL_NH, // powerhint:normal  DFS:veryhigh
    CAM_CAPTURE_E_LEVEL_NN, // powerhint:normal  DFS:normal
    CAM_CAPTURE_E_LEVEL_NL, // powerhint:normal  DFS:low
    CAM_CAPTURE_E_LEVEL_LN, // powerhint:low DFS:normal
    CAM_CAPTURE_E_LEVEL_LL, // powerhint:low  DFS:low
    CAM_CAPTURE_E_LEVEL_LH, // powerhint:low  DFS:veryhigh
    CAM_FLUSH_S,
    CAM_FLUSH_E,
    CAM_EXIT_S,
    CAM_EXIT_E,
} sys_performance_camera_scene;
複製程式碼

2.根據mTakePictureMode呼叫不同的拍照方法

  • 第一種:普通拍照模式 mTakePictureMode =SNAPSHOT_NO_ZSL_MODE 或者 SNAPSHOT_ONLY_MODE ret = takePicture();
  • 第二種:零延遲拍照(預覽畫面是啥,拍出來的就是啥,所見即所得) mTakePictureMode == SNAPSHOT_ZSL_MODE ret = zslTakePicture();
  • 第三種:視訊模式 mTakePictureMode == SNAPSHOT_VIDEO_MODE ret = VideoTakePicture();

有3條分支,這裡我們選擇普通的拍照模式分支繼續分析。

int SprdCamera3OEMIf::takePicture() {
···
    mHalOem->ops->camera_take_picture(mCameraHandle, mCaptureMode)
···
}
複製程式碼

其實takePicture函式有很多操作,比如:相機是否已經preview,沒有的話,先進行preview,其次,相機是否正在capturing(截圖),如果是的話,等待,直到capturing結束等等。

最後呼叫mHalOem->ops->camera_take_picture(mCameraHandle, mCaptureMode)來呼叫到oem層。

OEM層(展訊自己封裝的一層,Hal層和驅動層溝通的中間橋樑)

vendor/sprd/modules/libcamera/oem2v1/src/SprdOEMCamera.c

cmr_int camera_take_picture(cmr_handle camera_handle,
                            enum takepicture_mode cap_mode) {
···
    ret = camera_local_start_snapshot(camera_handle, cap_mode, CAMERA_SNAPSHOT);
    if (ret) {
        CMR_LOGE("failed to start snapshot %ld", ret);
    }    
···
}
複製程式碼

分析:這個函很簡單,就直接呼叫camera_local_start_snapshot進行拍照動作 vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_set_cap_size(cmr_handle oem_handle,
                                  cmr_u32 is_reprocessing, cmr_u32 camera_id,
                                  cmr_u32 width, cmr_u32 height) {
    //1
    ret = cmr_snapshot_post_proc(cxt->snp_cxt.snapshot_handle, &snp_param);
    //2
    ret = camera_local_start_capture(oem_handle);
    //3
    ret = cmr_snapshot_receive_data(cxt->snp_cxt.snapshot_handle,
                                        SNAPSHOT_EVT_CHANNEL_DONE,
                                       (void *)&frame);
}
複製程式碼

分析:該函式主要做了以下事情:

1.呼叫cmr_snapshot_post_proc()函式傳送一條msg訊息

2.呼叫camera_local_start_capture()函式繼續拍照流程

3.呼叫cmr_snapshot_receive_data()函式receive拍照的資料

先來看

1.呼叫cmr_snapshot_post_proc()函式傳送一條msg訊息


vendor/sprd/modules/libcamera/oem2v1/src/cmr_snapshot.c

cmr_int cmr_snapshot_post_proc(cmr_handle snapshot_handle,
                               struct snapshot_param *param_ptr) {
···
    message.msg_type = SNP_EVT_START_PROC;
    message.sync_flag = CMR_MSG_SYNC_PROCESSED;
    message.alloc_flag = 0;
    message.data = param_ptr;
    ret = cmr_thread_msg_send(cxt->thread_cxt.main_thr_handle, &message);
···
}
複製程式碼

訊息型別: message.msg_type = SNP_EVT_START_PROC;

執行緒處理函式為:snp_main_thread_proc

我們來看這個處理函式:

cmr_int snp_main_thread_proc(struct cmr_msg *message, void *p_data) {
···
    switch (message->msg_type) {
···
    case SNP_EVT_START_PROC:
        ret = snp_set_post_proc_param(snp_handle,
                                      (struct snapshot_param *)message->data);
        break;
···
    }
···
}
複製程式碼

分析:直接呼叫snp_set_post_proc_param函式

cmr_int snp_set_post_proc_param(cmr_handle snp_handle,
                                struct snapshot_param *param_ptr) {
···
    ret = cxt->ops.get_sensor_info(cxt->oem_handle, cxt->req_param.camera_id,
                                   &cxt->sensor_info);

    ret = snp_set_jpeg_dec_param(snp_handle);

    ret = snp_set_isp_proc_param(snp_handle);

    ret = snp_set_channel_out_param(snp_handle);
    ret = snp_set_hdr_param(snp_handle);

    snp_get_is_scaling(snp_handle, is_normal_cap);

    ret = snp_set_rot_param(snp_handle);

    ret = snp_set_jpeg_enc_param(snp_handle);

    ret = snp_set_jpeg_exif_param(snp_handle);
···
}
複製程式碼

分析:設定各種引數。


2.呼叫camera_local_start_capture()函式繼續拍照流程


vendor/sprd/modules/libcamera/oem2v1/src/cmr_oem.c

cmr_int camera_local_start_capture(cmr_handle oem_handle) {
    //設定拍照的時候是否需要閃光燈
    camera_local_snapshot_is_need_flash(oem_handle, cxt->camera_id,
                                        &flash_status);    
    //繼續呼叫cmr_grab_start_capture拍照
    ret = cmr_grab_start_capture(cxt->grab_cxt.grab_handle, capture_param);
}
複製程式碼

這裡繼續呼叫cmr_grab_start_capture拍照。 vendor/sprd/modules/libcamera/oem2v1/src/cmr_grab.c

cmr_int cmr_grab_start_capture(cmr_handle grab_handle,
                               struct sprd_img_capture_param capture_param) {
    struct cmr_grab *p_grab;

    p_grab = (struct cmr_grab *)grab_handle;

    ret = ioctl(p_grab->fd, SPRD_IMG_IO_START_CAPTURE, &capture_param);
···
}
複製程式碼

從這裡開始,就呼叫到我們驅動層了,通過ioctl的介面呼叫驅動的函式。那麼通過SPRD_IMG_IO_START_CAPTURE這個cmd呼叫的時哪個函式呢?

【kernel層】

kernel/drivers/misc/sprd_camera/dcam/dcam_if_r4p0/

static struct dcam_io_ctrl_fun s_cam_io_ctrl_fun_tab[] = {
···
{SPRD_IMG_IO_START_CAPTURE,		dcamio_start_capture},
···
}
複製程式碼

因此,可以看出呼叫的時dcamio_start_capture函式,好吧,我們就跟到kernel層一探究竟!!! kernel/drivers/misc/sprd_camera/dcam/dcam_if_r4p0/dcam_ioctrl.c

static int dcamio_start_capture(struct camera_file *camerafile,
             unsigned long arg, 
             unsigned int cmd) 
{
    int ret = 0; 
    unsigned int cap_flag = 0; 
    struct camera_dev *dev = NULL;
    struct camera_info *info = NULL;
    struct camera_group *group = NULL;
    //獲取裝置資訊
    ret = sprd_img_get_dcam_dev(camerafile, &dev, &info);
    if (ret) {
        pr_err("fail to get dcam dev\n");
        goto exit;
    }    

    group = camerafile->grp;
    //從使用者空間獲得資料,拷貝到cap_flag變數中
    ret = copy_from_user(&cap_flag, (void __user *) arg,
                 sizeof(unsigned int));
    if (ret) {
        pr_err("fail to get user info\n");
        ret = -EFAULT;
        goto exit;
    }
    
    if (dev->cap_flag == DCAM_CAPTURE_STOP) {
        dev->cap_flag = DCAM_CAPTURE_START;
        if (dev->dcam_cxt.need_isp_tool)
            cap_flag = DCAM_CAPTURE_NONE;
        pr_info("start capture, cap_flag %d\n", cap_flag);
        //呼叫該函式進行拍照動作
        ret = sprd_isp_start_pipeline_full(dev->isp_dev_handle,
                           cap_flag);
        if (ret) {
            pr_err("fail to start offline\n");
            goto exit;
        }
    }
    //拍照完成
    pr_info("start capture done\n");

exit:
    return ret;
}
複製程式碼

註釋新增的很清晰了,這裡簡單說一下 呼叫sprd_isp_start_pipeline_full動作去執行拍照,最後的資料會儲存在p_offline_frame中!【struct camera_frame *p_offline_frame = NULL,p_offline_frame是一個指標】

int sprd_isp_start_pipeline_full(···) {
···
    p_offline_frame = &dev->offline_frame[ISP_OFF_BUF_FULL];
    memcpy(p_offline_frame, &frame, sizeof(struct camera_frame));
    complete(&dev->offline_full_thread_com);
···
}
複製程式碼

最後通過complete喚醒執行緒,讓執行緒去接受資料。

PS:【complete是完成量的概念,用於保護共享資料,防止競態,並且告訴另一個休眠的執行緒,說我邊完事了,你醒醒,繼續幹你的活去。具體可以參考LDD這本書或者自行百度】

最後我們簡單分析一下是如何收取資料的

3.呼叫cmr_snapshot_receive_data()函式receive拍照的資料

vendor/sprd/modules/libcamera/oem2v1/src/cmr_snapshot.c

cmr_int cmr_snapshot_receive_data(cmr_handle snapshot_handle, cmr_int evt,
                                  void *data) {
···
    switch (evt) {//normol拍照模式
    case SNAPSHOT_EVT_CHANNEL_DONE:
        malloc_len = sizeof(struct frm_info);
        CMR_LOGD("video %d zsl %d yaddr_vir 0x%x",
                 cxt->req_param.is_video_snapshot,
                 cxt->req_param.is_zsl_snapshot, frame_info_ptr->yaddr_vir);
        buffer_id = snp_get_buffer_id(snapshot_handle, data);
        buffer_id += frame_info_ptr->base;

        snp_evt = SNP_EVT_CHANNEL_DONE;
        if (1 == cxt->req_param.is_video_snapshot ||
            1 == cxt->req_param.is_zsl_snapshot) {
            flag = 1;
            width = cxt->req_param.post_proc_setting.chn_out_frm[0].size.width;
            height =
                cxt->req_param.post_proc_setting.chn_out_frm[0].size.height;
            act_width = cxt->req_param.post_proc_setting.actual_snp_size.width;
            act_height =
                cxt->req_param.post_proc_setting.actual_snp_size.height;
            //memcpy指的是c和c++使用的記憶體拷貝函式,從kernel中通過地址拷貝資料到oem層
            memcpy(&chn_data, data, sizeof(struct frm_info));
            chn_data.base = CMR_CAP0_ID_BASE;
            chn_data.frame_id = CMR_CAP0_ID_BASE;
            if (1 == cxt->req_param.is_zsl_snapshot) {
                chn_data.base = CMR_CAP1_ID_BASE;
                chn_data.frame_id = CMR_CAP1_ID_BASE;
            }
        }

        if (1 == cxt->req_param.is_video_snapshot) {//視訊模式
        ···
            cmr_copy((void *)dst_vir, (void *)src_vir, width * height / 2);
            cmr_snapshot_memory_flush(
                cxt, &(cxt->req_param.post_proc_setting.chn_out_frm[0]));
        ···
        } else if (1 == cxt->req_param.is_zsl_snapshot) {//零延遲拍照模式
        ···
            cmr_copy((void *)dst_vir, (void *)src_vir, width * height / 2);
            cmr_snapshot_memory_flush(
                cxt, &(cxt->req_param.post_proc_setting.chn_out_frm[0]));
        ···
        }
        break;
 ···
    }
···
}
複製程式碼

拍完照片後,我們會受到一個msg訊息,type=SNAPSHOT_EVT_CHANNEL_DONE,表示拍照完成!

呼叫memcpy(&chn_data, data, sizeof(struct frm_info));從kernel獲取資料,

還記得kernel中是吧資料儲存在 struct camera_frame *p_offline_frame 指標中,

我們通過地址把資料拷貝出來! 即memcpy(&chn_data, data, sizeof(struct frm_info));!

到此,完結!!!

相關文章