MT6739的Android9.0 Camera kernel 驱动

2023-11-17

文章目录
Kernel 层驱动的实现
Camera 开机流程
Camera 驱动的文件结构
Camera 驱动初始化流程
Camera 入口函数 imgsensor_init
注册的平台驱动结构体 gimgsensor_platform_driver
imgsensor_probe 探测函数的实现
imgsensor_driver_register 注册函数的实现
gimgsensor_file_operations 文件操作结构体的实现
imgsensor_ioctl 的定义
adopt_CAMERA_HW_FeatureControl 的定义
imgsensor_set_driver 的实现
kdSensorList 结构体中的实现
GC2385_MIPI_RAW_SensorInit 的实现
sensor_func 的实现
open 的实现
feature_control 的实现
imgsensor_i2c_init 的实现
imgsensor_hw_init 的实现
imgsensor_i2c_create() 的实现
gi2c_driver 结构体的实现
设备树中的配置
cust.dtsi 中对camera的配置
k39tv1_bsp.dws 中对camera的配置
camera驱动文件的执行流程
总结
 

Kernel 层驱动的实现


camera 整个驱动框架分为三个部分: hal层 逻辑调用,kernel层 的通用驱动 sensorlist.c 和 具体IC 的驱动, 比如 gc2385_mipi_raw.c ,kernel 起来后不会直接去访问 硬件sensor ,而是会注册相关的驱动,之后 Android系统 起来后会启动相关的服务如: camera_service ,在 camera 服务中会直接去访问 hal层 , kernel驱动 ,进而操作 camera 。这里只分析 kernel层中 camera驱动的实现。
 

Camera 开机流程


Power On 上电开机,然后通过 i2c 地址匹配 i2c 通讯, reset 和 Power Down 上电 (上电代码在 kd_camera_hw.c 中的 kdCISModulePowerOn , VCAM ** 主要给 ISP 供电; VCAM_IO 是数字IO电源,主要给I2C** 供电, VCAMA 是模拟供电,主要给 感光区 和 ADC 部分供电, VCAMAF 主要给对焦马达供电;具体使用可以根据 datasheet 添加,有时会影响 cts ),读取 sensor ID (具体 ic 驱动里面的 open 和 get_imgsensor_id 都有读取 id 的操作, sensor id 只要大于 0 、小于 0xffffffff 都是合法的),然后软复位,下载 preview 参数为预览做准备,下载 capture 为拍照做准备,然后执行下电操作。

Camera 驱动的文件结构


参考 MTK 的 mt6739_Sensor_Porting_guide 可以发现,在 kernel-3.18 中的 camera 驱动有 kd_sensorlist.c 和 kd_camera_hw.c 这两个文件,但是在 MT6739 平台的 kernel-4.4 中将这个文件拆分成了多个文件,如下图所示:

各个文件功能描述

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_hw.c

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_i2c.c

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_proc.c

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_sensor_list.c

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_legacy.c

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/mt6739/camera_hw/imgsensor_cfg_table.c
 

File Decription(英文) Decription(中文)
imgsensor.c Sensor driver adapter and driver entry point sensor 驱动适配器和驱动入口函数的实现
imgsensor_hw.c Sensor power control sensor 电源控制的实现
imgsensor_i2c.c I2C read/write I2C 读写函数的实现
imgsensor_proc.c PROC related part proc文件系统相关部分的实现
imgsensor_sensor_list.c List of all sensors init function 包含所有sensor初始化函数的表单
imgsensor_legacy.c Legacy part of sensor. Mainly I2c related API sensor的旧的接口部分。主要是与I2c相关API
imgsensor_cfg_table.c Sensor Power and I2C configruation table sensor电源和I2C的配置表

Camera 驱动初始化流程

Camera 入口函数 imgsensor_init

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

module_init(imgsensor_init);

/* camera sensor入口函数 */
static int __init imgsensor_init(void)
{
    PK_DBG("[camerahw_probe] start\n");

    /* 注册一个平台驱动gimgsensor_platform_driver */
    if (platform_driver_register(&gimgsensor_platform_driver)) {
        PK_PR_ERR("failed to register CAMERA_HW driver\n");
        return -ENODEV;
    }
    ...
        
    return 0;
}

module_init(imgsensor_init);

/* camera sensor入口函数 */
static int __init imgsensor_init(void)
{
    PK_DBG("[camerahw_probe] start\n");

    /* 注册一个平台驱动gimgsensor_platform_driver */
    if (platform_driver_register(&gimgsensor_platform_driver)) {
        PK_PR_ERR("failed to register CAMERA_HW driver\n");
        return -ENODEV;
    }
    ...
        
    return 0;
}
 

注册的平台驱动结构体 gimgsensor_platform_driver

#ifdef CONFIG_OF /* 通过设备树进行match */
static const struct of_device_id gimgsensor_of_device_id[] = {
    { .compatible = "mediatek,camera_hw", },
    {}
};
#endif

static struct platform_driver gimgsensor_platform_driver = {
    .probe      = imgsensor_probe, /* 匹配成功会调用这个探测函数 */
    .remove     = imgsensor_remove,
    .suspend    = imgsensor_suspend,
    .resume     = imgsensor_resume,
    .driver     = {
        .name   = "image_sensor",
        .owner  = THIS_MODULE,
#ifdef CONFIG_OF
        .of_match_table = gimgsensor_of_device_id, /* 保存与设备树compatible进行匹配的字符串 */
#endif
    }
};
 

当 platform_devices 和 platform_driver 通过 match 函数匹配上后,会调用 imgsensor_probe 函数注册前后摄 camera 驱动。

 

imgsensor_probe 探测函数的实现

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

 

/* 平台driver成功与device匹配后调用imgsensor_probe */
static int imgsensor_probe(struct platform_device *pdev)
{
    /* 分配设置注册camera驱动的字符设备文件/dev/kd_camera_hw,创建主次设备号 */
    if (imgsensor_driver_register()) {
        PK_PR_ERR("[CAMERA_HW] register char device failed!\n");
        return -1;
    }

    gpimgsensor_hw_platform_device = pdev;

    ... /* 省略部分代码 */
    
    /* camera硬件初始化,包含对power引脚的配置 */
    imgsensor_hw_init(&pgimgsensor->hw);
    
    /* 注册前后摄像头驱动 */
    imgsensor_i2c_create();
    imgsensor_proc_init();

    atomic_set(&pgimgsensor->imgsensor_open_cnt, 0);
#ifdef CONFIG_MTK_SMI_EXT
    mmdvfs_register_mmclk_switch_cb(mmsys_clk_change_cb, MMDVFS_CLIENT_ID_ISP);
#endif

    return 0;
}

 

在 imgsensor_probe() 函数中一开始先为 camera 驱动分配设置注册了 camera 字符驱动设备,然后通过函数 imgsensor_i2c_create() 注册前后 camera 驱动。

imgsensor_driver_register 注册函数的实现


imgsensor_driver_register 由函数 imgsensor_probe() 调用,在 imgsensor_driver_register() 函数中实现了 camera 驱动的主次设备号创建,注册对应的设备文件操作结构体 gimgsensor_file_operations 。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
 

static inline int imgsensor_driver_register(void)
{
    int i, error = 0;
    dev_t dev_no = MKDEV(IMGSENSOR_DEVICE_NNUMBER, 0);

    /* 申请主设备号 */
    /*************************************************************** 
     * 让内核分配给我们一个尚未使用的主设备号,不是由我们自己指定的
     *
     * dev      : alloc_chrdev_region函数向内核申请下来的主设备号
     * baseminor: 次设备号的起始
     * count    : 申请次设备号的个数
     * name     : 执行 cat /proc/devices显示的名称
     ***************************************************************/
    if (alloc_chrdev_region(&dev_no, 0, 1, IMGSENSOR_DEV_NAME)) {
        PK_DBG("[CAMERA SENSOR] Allocate device no failed\n");
        return -EAGAIN;
    }

    /* Allocate driver */
    /* 给 gpimgsensor_cdev 分配内存,会在 cdev_del 中自动释放 */
    gpimgsensor_cdev = cdev_alloc();
    if (gpimgsensor_cdev ==  NULL) {
        unregister_chrdev_region(dev_no, 1);
        PK_DBG("[CAMERA SENSOR] Allocate mem for kobject failed\n");
        return -ENOMEM;
    }

    /* Attatch file operation. */
    /*************************************************************
     * cdev_init 与 cdev_del 函数基本一致,但是多出来对 cdev->ops
     * 的赋值,用来将 gimgsensor_file_operations 加入到系统中,
     * gimgsensor_file_operations 中包含着实际处理与设备通信的
     * 函数。
     *************************************************************/
    cdev_init(gpimgsensor_cdev, &gimgsensor_file_operations);

    gpimgsensor_cdev->owner = THIS_MODULE;

    /* Add to system */
    /*************************************************************
     * 初始化 cdev 后,需要通过 cdev_add 把它添加到系统中去。
     * 传入 cdev 结构的指针 gpimgsensor_cdev , 起始设备编号,
     * 以及设备编号范围。 
     *
     * 可以在 /dev/ 目录下找到设备文件 "kd_camera_hw"
     *************************************************************/
    if (cdev_add(gpimgsensor_cdev, dev_no, 1)) {
        PK_DBG("Attatch file operation failed\n");
        unregister_chrdev_region(dev_no, 1);
        return -EAGAIN;
    }

    /*************************************************************
     * class_create 动态创建设备的逻辑类,并完成部分字段的初始化,
     * 然后将其添加到内核中。创建的逻辑类位于/sys/class/。 
     *
     * owner: 拥有者。一般赋值为THIS_MODULE。
     * name: 创建的逻辑类的名称。
     *************************************************************/
    gpimgsensor_class = class_create(THIS_MODULE, "sensordrv");
    if (IS_ERR(gpimgsensor_class)) {
        int ret = PTR_ERR(gpimgsensor_class);

        PK_DBG("Unable to create class, err = %d\n", ret);
        return ret;
    }

    /************************************************************
     * 通过 device_create 在 /sys/class/sensordrv/ 目录下创建一个
     * 设备文件目录,目录名为 "kd_camera_hw" 
     ************************************************************/
    gimgsensor_device = device_create(gpimgsensor_class, NULL, dev_no, NULL, IMGSENSOR_DEV_NAME);

    if (!gimgsensor_device) {
        pr_err("Failed to create kd_camera_hw device\n");
        return -1;
    }
    
    for (i = 0; i < ARRAY_SIZE(device_attrs); i++) {
        error = device_create_file(gimgsensor_device, &device_attrs[i]);

        if (error) {
            PK_DBG("device_attrs[%d] create failed!!!\n", i);
            break;
        }
    }

    return 0;
}
 

通过 cdev_init 函数将 gimgsensor_file_operations 设备文件操作结构体注册内核,这样就可以让上层应用调用时使用到底层相关的 open、read、write、ioctl 函数。

 

gimgsensor_file_operations 文件操作结构体的实现


imgsensor_ioctl 函数被赋值给 file_operations 结构体 gimgsensor_file_operations 的成员变量 .unlocked_ioctl 。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
static const struct file_operations gimgsensor_file_operations = {
    .owner = THIS_MODULE,
    .open = imgsensor_open,
    .release = imgsensor_release,
    .unlocked_ioctl = imgsensor_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = imgsensor_compat_ioctl
#endif
};
 

在 gimgsensor_file_operations 中可以发现,camera 控制函数是有 imgsensor_ioctl 实现的。

imgsensor_ioctl 的定义


通过 imgsensor_ioctl 这个函数来提供 camera 硬件驱动的控制接口。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c
 

static long imgsensor_ioctl(
    struct file *a_pstFile,
    unsigned int a_u4Command,
    unsigned long a_u4Param)
{
    int i4RetValue = 0;
    void *pBuff = NULL;
    ...

    switch (a_u4Command) {
    case KDIMGSENSORIOC_X_GET_CONFIG_INFO:
        i4RetValue = adopt_CAMERA_HW_GetInfo(pBuff);
        break;
    case KDIMGSENSORIOC_X_GETINFO2:
        i4RetValue = adopt_CAMERA_HW_GetInfo2(pBuff);
        break;
    case KDIMGSENSORIOC_X_FEATURECONCTROL:
        i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
        break;
    case KDIMGSENSORIOC_X_CONTROL:
        i4RetValue = adopt_CAMERA_HW_Control(pBuff);
        break;
    case KDIMGSENSORIOC_X_SET_MCLK_PLL:
        i4RetValue = imgsensor_clk_set(&pgimgsensor->clk, (ACDK_SENSOR_MCLK_STRUCT *)pBuff);
        break;
    case KDIMGSENSORIOC_X_GET_ISP_CLK:
/*E1(High):490, (Medium):364, (low):273*/
#define ISP_CLK_LOW    273
#define ISP_CLK_MEDIUM 364
#define ISP_CLK_HIGH   490
#ifdef CONFIG_MTK_SMI_EXT
        PK_DBG("KDIMGSENSORIOC_X_GET_ISP_CLK current_mmsys_clk=%d\n", current_mmsys_clk);
        if (mmdvfs_get_stable_isp_clk() == MMSYS_CLK_HIGH)
            *(unsigned int *)pBuff = ISP_CLK_HIGH;
        else if (mmdvfs_get_stable_isp_clk() == MMSYS_CLK_MEDIUM)
            *(unsigned int *)pBuff = ISP_CLK_MEDIUM;
        else
            *(unsigned int *)pBuff = ISP_CLK_LOW;
#else
        *(unsigned int *)pBuff = ISP_CLK_HIGH;
#endif
        break;
    case KDIMGSENSORIOC_X_GET_CSI_CLK:
        i4RetValue = imgsensor_clk_ioctrl_handler(pBuff);
        break;
    case KDIMGSENSORIOC_T_OPEN:
    case KDIMGSENSORIOC_T_CLOSE:
    case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
    case KDIMGSENSORIOC_X_SET_DRIVER:
    case KDIMGSENSORIOC_X_GETRESOLUTION2:
    case KDIMGSENSORIOC_X_GET_SOCKET_POS:
    case KDIMGSENSORIOC_X_SET_GPIO:
    case KDIMGSENSORIOC_X_SET_I2CBUS:
    case KDIMGSENSORIOC_X_RELEASE_I2C_TRIGGER_LOCK:
    case KDIMGSENSORIOC_X_SET_SHUTTER_GAIN_WAIT_DONE:
    case KDIMGSENSORIOC_X_SET_CURRENT_SENSOR:
        i4RetValue = 0;
        break;
    default:
        PK_DBG("No such command %d\n", a_u4Command);
        i4RetValue = -EPERM;
        break;
    }

    if ((_IOC_READ & _IOC_DIR(a_u4Command)) &&
            copy_to_user((void __user *) a_u4Param,
                          pBuff,
                        _IOC_SIZE(a_u4Command))) {
        kfree(pBuff);
        PK_DBG("[CAMERA SENSOR] ioctl copy to user failed\n");
        i4RetValue =  -EFAULT;
        goto CAMERA_HW_Ioctl_EXIT;
    }

    kfree(pBuff);
CAMERA_HW_Ioctl_EXIT:
    return i4RetValue;
}

对 camera 硬件的初始化操作是在函数 adopt_CAMERA_HW_FeatureControl() 中实现的。

adopt_CAMERA_HW_FeatureControl 的定义
adopt_CAMERA_HW_FeatureControl 由函数 imgsensor_ioctl() 调用。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

static inline int adopt_CAMERA_HW_FeatureControl(void *pBuf)
{
    ...
    switch (pFeatureCtrl->FeatureId) {
    case SENSOR_FEATURE_OPEN:
        ret = imgsensor_sensor_open(psensor);
        break;
    case SENSOR_FEATURE_CLOSE:
        ret = imgsensor_sensor_close(psensor);
        /* reset the delay frame flag */
        break;
            
    case SENSOR_FEATURE_SET_DRIVER:
    {
        MINT32 drv_idx;

        psensor->inst.sensor_idx = pFeatureCtrl->InvokeCamera;
        drv_idx = imgsensor_set_driver(psensor);
        memcpy(pFeaturePara, &drv_idx, FeatureParaLen);

        break;
    }
    ...

通过 cdev_init 函数将 gimgsensor_file_operations 设备文件操作结构体注册内核,这样就可以让上层应用调用时使用到底层相关的 open、read、write、ioctl 函数。

gimgsensor_file_operations 文件操作结构体的实现
imgsensor_ioctl 函数被赋值给 file_operations 结构体 gimgsensor_file_operations 的成员变量 .unlocked_ioctl 。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

static const struct file_operations gimgsensor_file_operations = {
    .owner = THIS_MODULE,
    .open = imgsensor_open,
    .release = imgsensor_release,
    .unlocked_ioctl = imgsensor_ioctl,
#ifdef CONFIG_COMPAT
    .compat_ioctl = imgsensor_compat_ioctl
#endif
};

在 gimgsensor_file_operations 中可以发现,camera 控制函数是有 imgsensor_ioctl 实现的。

imgsensor_ioctl 的定义
通过 imgsensor_ioctl 这个函数来提供 camera 硬件驱动的控制接口。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

static long imgsensor_ioctl(
    struct file *a_pstFile,
    unsigned int a_u4Command,
    unsigned long a_u4Param)
{
    int i4RetValue = 0;
    void *pBuff = NULL;
    ...

    switch (a_u4Command) {
    case KDIMGSENSORIOC_X_GET_CONFIG_INFO:
        i4RetValue = adopt_CAMERA_HW_GetInfo(pBuff);
        break;
    case KDIMGSENSORIOC_X_GETINFO2:
        i4RetValue = adopt_CAMERA_HW_GetInfo2(pBuff);
        break;
    case KDIMGSENSORIOC_X_FEATURECONCTROL:
        i4RetValue = adopt_CAMERA_HW_FeatureControl(pBuff);
        break;
    case KDIMGSENSORIOC_X_CONTROL:
        i4RetValue = adopt_CAMERA_HW_Control(pBuff);
        break;
    case KDIMGSENSORIOC_X_SET_MCLK_PLL:
        i4RetValue = imgsensor_clk_set(&pgimgsensor->clk, (ACDK_SENSOR_MCLK_STRUCT *)pBuff);
        break;
    case KDIMGSENSORIOC_X_GET_ISP_CLK:
/*E1(High):490, (Medium):364, (low):273*/
#define ISP_CLK_LOW    273
#define ISP_CLK_MEDIUM 364
#define ISP_CLK_HIGH   490
#ifdef CONFIG_MTK_SMI_EXT
        PK_DBG("KDIMGSENSORIOC_X_GET_ISP_CLK current_mmsys_clk=%d\n", current_mmsys_clk);
        if (mmdvfs_get_stable_isp_clk() == MMSYS_CLK_HIGH)
            *(unsigned int *)pBuff = ISP_CLK_HIGH;
        else if (mmdvfs_get_stable_isp_clk() == MMSYS_CLK_MEDIUM)
            *(unsigned int *)pBuff = ISP_CLK_MEDIUM;
        else
            *(unsigned int *)pBuff = ISP_CLK_LOW;
#else
        *(unsigned int *)pBuff = ISP_CLK_HIGH;
#endif
        break;
    case KDIMGSENSORIOC_X_GET_CSI_CLK:
        i4RetValue = imgsensor_clk_ioctrl_handler(pBuff);
        break;
    case KDIMGSENSORIOC_T_OPEN:
    case KDIMGSENSORIOC_T_CLOSE:
    case KDIMGSENSORIOC_T_CHECK_IS_ALIVE:
    case KDIMGSENSORIOC_X_SET_DRIVER:
    case KDIMGSENSORIOC_X_GETRESOLUTION2:
    case KDIMGSENSORIOC_X_GET_SOCKET_POS:
    case KDIMGSENSORIOC_X_SET_GPIO:
    case KDIMGSENSORIOC_X_SET_I2CBUS:
    case KDIMGSENSORIOC_X_RELEASE_I2C_TRIGGER_LOCK:
    case KDIMGSENSORIOC_X_SET_SHUTTER_GAIN_WAIT_DONE:
    case KDIMGSENSORIOC_X_SET_CURRENT_SENSOR:
        i4RetValue = 0;
        break;
    default:
        PK_DBG("No such command %d\n", a_u4Command);
        i4RetValue = -EPERM;
        break;
    }

    if ((_IOC_READ & _IOC_DIR(a_u4Command)) &&
            copy_to_user((void __user *) a_u4Param,
                          pBuff,
                        _IOC_SIZE(a_u4Command))) {
        kfree(pBuff);
        PK_DBG("[CAMERA SENSOR] ioctl copy to user failed\n");
        i4RetValue =  -EFAULT;
        goto CAMERA_HW_Ioctl_EXIT;
    }

    kfree(pBuff);
CAMERA_HW_Ioctl_EXIT:
    return i4RetValue;
}

对 camera 硬件的初始化操作是在函数 adopt_CAMERA_HW_FeatureControl() 中实现的。

adopt_CAMERA_HW_FeatureControl 的定义
adopt_CAMERA_HW_FeatureControl 由函数 imgsensor_ioctl() 调用。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

static inline int adopt_CAMERA_HW_FeatureControl(void *pBuf)
{
    ...
    switch (pFeatureCtrl->FeatureId) {
    case SENSOR_FEATURE_OPEN:
        ret = imgsensor_sensor_open(psensor);
        break;
    case SENSOR_FEATURE_CLOSE:
        ret = imgsensor_sensor_close(psensor);
        /* reset the delay frame flag */
        break;
            
    case SENSOR_FEATURE_SET_DRIVER:
    {
        MINT32 drv_idx;

        psensor->inst.sensor_idx = pFeatureCtrl->InvokeCamera;
        drv_idx = imgsensor_set_driver(psensor);
        memcpy(pFeaturePara, &drv_idx, FeatureParaLen);

        break;
    }
    ...


imgsensor_set_driver 是在文件 imgsensor.c 中的 adopt_CAMERA_HW_FeatureControl 函数中被调用的 ,即在开机过程中,vendor 会寻找 Sensor ,并对它做些硬件初始化。

imgsensor_set_driver 的实现
imgsensor_set_driver() 在函数 adopt_CAMERA_HW_FeatureControl() 中被调用。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor.c

int imgsensor_set_driver(struct IMGSENSOR_SENSOR *psensor)
{
    u32 drv_idx = 0;
    int ret = -EIO;

    struct IMGSENSOR_SENSOR_INST    *psensor_inst = &psensor->inst;
    /* kdSensorList 中包含操作具体硬件的摄像头驱动代码 */
    struct IMGSENSOR_INIT_FUNC_LIST *pSensorList  = kdSensorList;
#define TOSTRING(value)           #value
#define STRINGIZE(stringizedName) TOSTRING(stringizedName)

    char *psensor_list_with_end = NULL;
    char *sensor_kconfig = STRINGIZE(CONFIG_CUSTOM_KERNEL_IMGSENSOR);

    static int orderedSearchList[MAX_NUM_OF_SUPPORT_SENSOR] = {-1};
    static bool get_search_list = true;
    int i = 0;
    int j = 0;
    char *driver_name = NULL;
    const char *pDTS_sensors = NULL;
    struct device_node *of_node = of_find_compatible_node(NULL, NULL, 
                                                          "mediatek,camera_hw");

    imgsensor_mutex_init(psensor_inst);
    imgsensor_i2c_init(&psensor_inst->i2c_cfg, imgsensor_custom_config[psensor->inst.sensor_idx].i2c_dev);
    imgsensor_i2c_filter_msg(&psensor_inst->i2c_cfg, true);

    if (get_search_list) {
        psensor_list_with_end = kmalloc(strlen(sensor_kconfig)-1, GFP_KERNEL);
    }
    if (psensor_list_with_end != NULL) {
        for (j = 0; j < MAX_NUM_OF_SUPPORT_SENSOR; j++)
            orderedSearchList[j] = -1;

        memcpy(psensor_list_with_end, sensor_kconfig+1, strlen(sensor_kconfig)-2);
        *(psensor_list_with_end+strlen(sensor_kconfig)-2) = '\0';
        of_property_read_string(of_node, "enable-sensor", &pDTS_sensors);

        PK_DBG("psensor_list_with_end %s ,pDTS_sensors %s\n",
            psensor_list_with_end, pDTS_sensors == NULL ? "null" : pDTS_sensors);
        driver_name = strsep(&psensor_list_with_end, " \0");

        while (driver_name != NULL) {
            for (j = 0; j < MAX_NUM_OF_SUPPORT_SENSOR; j++) {
                if (pSensorList[j].init == NULL)
                    break;
                else if (!strcmp(driver_name, pSensorList[j].name)) {
                    if (pDTS_sensors != NULL && !strstr(pDTS_sensors, driver_name))
                        continue;
                    orderedSearchList[i++] = j;
                    break;
                }
            }
            driver_name = strsep(&psensor_list_with_end, " \0");
        }
        get_search_list = false;
        kfree(psensor_list_with_end);
    }

    for (i = 0; i < MAX_NUM_OF_SUPPORT_SENSOR; i++) {
        /*PK_DBG("orderedSearchList[%d]=%d\n", i, orderedSearchList[i]);*/
        if (orderedSearchList[i] == -1)
            continue;
        drv_idx = orderedSearchList[i];
        if (pSensorList[drv_idx].init) {
            pSensorList[drv_idx].init(&psensor->pfunc);
            if (psensor->pfunc) {
                /* get sensor name */
                psensor_inst->psensor_name = (char *)pSensorList[drv_idx].name;
#ifdef IMGSENSOR_LEGACY_COMPAT
                psensor_inst->status.arch = psensor->pfunc->arch;
#endif
                if (!imgsensor_check_is_alive(psensor)) {
                    PK_INFO("[imgsensor_set_driver] :[%d][%d][%s]\n",
                                psensor->inst.sensor_idx,
                                drv_idx,
                                psensor_inst->psensor_name);

                    ret = drv_idx;
                    break;
                }
            } else {
                PK_PR_ERR("ERROR:NULL g_pInvokeSensorFunc[%d][%d]\n",
                            psensor->inst.sensor_idx,
                            drv_idx);
            }
        } else {
            PK_PR_ERR("ERROR:NULL sensor list[%d]\n", drv_idx);
        }

    }
    imgsensor_i2c_filter_msg(&psensor_inst->i2c_cfg, false);

    return ret;
}

kdSensorList 在文件 imgsensor.c 中被函数 imgsensor_set_driver() 调用获取设备信息。

kdSensorList 结构体中的实现
kdSensorList 在函数 imgsensor_set_driver 中被调用, imgsensor_sensor_list.c 存放 sensor 的 id , 比如 gc2385 的驱动信息

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_sensor_list.c

struct IMGSENSOR_INIT_FUNC_LIST kdSensorList[MAX_NUM_OF_SUPPORT_SENSOR] = {
    
    ... /* 省略部分代码 */

#if defined(GC2385_MIPI_RAW)
    {GC2385_SENSOR_ID, SENSOR_DRVNAME_GC2385_MIPI_RAW, GC2385_MIPI_RAW_SensorInit},
#endif
    
#if defined(GC5035CMIPI_RAW)
    {GC5035CMIPI_SENSOR_ID, SENSOR_DRVNAME_GC5035C_MIPI_RAW, GC5035CMIPI_RAW_SensorInit},
#endif
    ... /* 省略部分代码 */
        
    /*  ADD sensor driver before this line */
    {0, {0}, NULL}, /* end of list */
};

GC2385_MIPI_RAW_SensorInit 的实现
GC2385_MIPI_RAW_SensorInit 在结构体 kdSensorList 中被调用。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsnesor/src/mt6739/gc2385_mipi_raw.c

UINT32 GC2385_MIPI_RAW_SensorInit(PSENSOR_FUNCTION_STRUCT *pfFunc)
{
    /* To Do : Check Sensor status here */
    if (pfFunc!=NULL)
        *pfFunc=&sensor_func;
    return ERROR_NONE;
}    /*    GC2385MIPI_RAW_SensorInit    */

重点关注结构体 sensor_func。

sensor_func 的实现
static SENSOR_FUNCTION_STRUCT sensor_func = {
    open,
    get_info,
    get_resolution,
    feature_control,
    control,
    close
};

open 的实现
该函数实现了对 CMOS sensor 的初始化和注册

static kal_uint32 open(void)
{
    kal_uint8 i = 0;
    kal_uint8 retry = 2;
    kal_uint32 sensor_id = 0;
    LOG_1;
  
    while (imgsensor_info.i2c_addr_table[i] != 0xff) {
        spin_lock(&imgsensor_drv_lock);
        imgsensor.i2c_write_id = imgsensor_info.i2c_addr_table[i];
        spin_unlock(&imgsensor_drv_lock);
        do {
            /* 通过I2C协议由寄存器中读取sensor id */
            sensor_id = return_sensor_id()&0xffef;
            /* 根据回读的sensor id与设定值是否相等来判断是否添加了对应的sensor驱动 */
            if (sensor_id == imgsensor_info.sensor_id) {
                LOG_INF("i2c write id: 0x%x, sensor id: 0x%x\n", 
                                             imgsensor.i2c_write_id,sensor_id);
                break;
            }
            LOG_INF("Read sensor id fail, write id: 0x%x, id: 0x%x\n", 
                                            imgsensor.i2c_write_id,sensor_id);
            retry--;
        } while(retry > 0);
        i++;
        if (sensor_id == imgsensor_info.sensor_id)
            break;
        retry = 2;
    }
    if (imgsensor_info.sensor_id != sensor_id)
        return ERROR_SENSOR_CONNECT_FAIL;

    /* initail sequence write in  */
    /* 这里具体对应着IC的初始化数据 */
    sensor_init();

    spin_lock(&imgsensor_drv_lock);

    imgsensor.autoflicker_en= KAL_FALSE;
    imgsensor.sensor_mode = IMGSENSOR_MODE_INIT;
    imgsensor.pclk = imgsensor_info.pre.pclk;
    imgsensor.frame_length = imgsensor_info.pre.framelength;
    imgsensor.line_length = imgsensor_info.pre.linelength;
    imgsensor.min_frame_length = imgsensor_info.pre.framelength;
    imgsensor.dummy_pixel = 0;
    imgsensor.dummy_line = 0;
    imgsensor.ihdr_en = 0;
    imgsensor.test_pattern = KAL_FALSE;
    imgsensor.current_fps = imgsensor_info.pre.max_framerate;
    spin_unlock(&imgsensor_drv_lock);
    GC2385DuringTestPattern = KAL_FALSE; 

    return ERROR_NONE;
}    /*    open  */

feature_control 的实现
static kal_uint32 feature_control(MSDK_SENSOR_FEATURE_ENUM feature_id,
                             UINT8 *feature_para,UINT32 *feature_para_len)
{
    UINT16 *feature_return_para_16=(UINT16 *) feature_para;
    UINT16 *feature_data_16=(UINT16 *) feature_para;
    UINT32 *feature_return_para_32=(UINT32 *) feature_para;
    UINT32 *feature_data_32=(UINT32 *) feature_para;
    unsigned long long *feature_data=(unsigned long long *) feature_para;
    //unsigned long long *feature_return_para=(unsigned long long *) feature_para;

    SENSOR_WINSIZE_INFO_STRUCT *wininfo;
    MSDK_SENSOR_REG_INFO_STRUCT *sensor_reg_data
                                        =(MSDK_SENSOR_REG_INFO_STRUCT *) feature_para;

    //printk("feature_id = %d\n", feature_id);
    switch (feature_id) {
        case SENSOR_FEATURE_GET_PERIOD:
            *feature_return_para_16++ = imgsensor.line_length;
            *feature_return_para_16 = imgsensor.frame_length;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_GET_PIXEL_CLOCK_FREQ:
            LOG_INF("feature_Control imgsensor.pclk = %d,imgsensor.current_fps = %d\n", 
                                                   imgsensor.pclk,imgsensor.current_fps);
            *feature_return_para_32 = imgsensor.pclk;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_SET_ESHUTTER:
            set_shutter(*feature_data);
            break;
        case SENSOR_FEATURE_SET_NIGHTMODE:
            night_mode((BOOL) *feature_data);
            break;
        case SENSOR_FEATURE_SET_GAIN:
            set_gain((UINT16) *feature_data);
            break;
        case SENSOR_FEATURE_SET_FLASHLIGHT:
            break;
        case SENSOR_FEATURE_SET_ISP_MASTER_CLOCK_FREQ:
            break;
        case SENSOR_FEATURE_SET_REGISTER:
            write_cmos_sensor(sensor_reg_data->RegAddr, sensor_reg_data->RegData);
            break;
        case SENSOR_FEATURE_GET_REGISTER:
            sensor_reg_data->RegData = read_cmos_sensor(sensor_reg_data->RegAddr);
            break;
        case SENSOR_FEATURE_GET_LENS_DRIVER_ID:
            /*****************************************************
             * get the lens driver ID from EEPROM or just return                    
             * LENS_DRIVER_ID_DO_NOT_CARE
             * if EEPROM does not exist in camera module.
             *****************************************************/
            *feature_return_para_32=LENS_DRIVER_ID_DO_NOT_CARE;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_SET_VIDEO_MODE:
            set_video_mode(*feature_data);
            break;
        case SENSOR_FEATURE_CHECK_SENSOR_ID:
            get_imgsensor_id(feature_return_para_32);
            break;
        case SENSOR_FEATURE_SET_AUTO_FLICKER_MODE:
            set_auto_flicker_mode((BOOL)*feature_data_16,*(feature_data_16+1));
            break;
        case SENSOR_FEATURE_SET_MAX_FRAME_RATE_BY_SCENARIO:
            set_max_framerate_by_scenario((MSDK_SCENARIO_ID_ENUM)*feature_data, 
                                                                      *(feature_data+1));
            break;
        case SENSOR_FEATURE_GET_DEFAULT_FRAME_RATE_BY_SCENARIO:
            get_default_framerate_by_scenario((MSDK_SCENARIO_ID_ENUM)*(feature_data), 
                                              (MUINT32 *)(uintptr_t)(*(feature_data+1)));
            break;
        case SENSOR_FEATURE_SET_TEST_PATTERN:
            set_test_pattern_mode((BOOL)*feature_data);
            break;
        //for factory mode auto testing
        case SENSOR_FEATURE_GET_TEST_PATTERN_CHECKSUM_VALUE: 
            *feature_return_para_32 = imgsensor_info.checksum_value;
            *feature_para_len=4;
            break;
        case SENSOR_FEATURE_SET_FRAMERATE:
            LOG_INF("current fps :%d\n", (UINT32)*feature_data);
            spin_lock(&imgsensor_drv_lock);
            imgsensor.current_fps = *feature_data;
            spin_unlock(&imgsensor_drv_lock);
            break;
        case SENSOR_FEATURE_SET_HDR:
            LOG_INF("ihdr enable :%d\n", (BOOL)*feature_data);
            spin_lock(&imgsensor_drv_lock);
            imgsensor.ihdr_en = (BOOL)*feature_data;
            spin_unlock(&imgsensor_drv_lock);
            break;
        case SENSOR_FEATURE_GET_CROP_INFO:
            LOG_INF("SENSOR_FEATURE_GET_CROP_INFO scenarioId:%d\n", 
                                                            (UINT32)*feature_data);

            wininfo = (SENSOR_WINSIZE_INFO_STRUCT *)(uintptr_t)(*(feature_data+1));

            switch (*feature_data_32) {
                case MSDK_SCENARIO_ID_CAMERA_CAPTURE_JPEG:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[1],
                                                       sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_VIDEO_PREVIEW:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[2],
                                                       sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_HIGH_SPEED_VIDEO:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[3],
                                                       sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_SLIM_VIDEO:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[4],
                                                       sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
                case MSDK_SCENARIO_ID_CAMERA_PREVIEW:
                default:
                    memcpy((void *)wininfo,(void *)&imgsensor_winsize_info[0],
                                                       sizeof(SENSOR_WINSIZE_INFO_STRUCT));
                    break;
            }
        case SENSOR_FEATURE_SET_IHDR_SHUTTER_GAIN:
            LOG_INF("SENSOR_SET_SENSOR_IHDR LE=%d, SE=%d, Gain=%d\n",(UINT16)*feature_data,(UINT16)*(feature_data+1),(UINT16)*(feature_data+2));
            ihdr_write_shutter_gain((UINT16)*feature_data,(UINT16)*(feature_data+1),
                                                            (UINT16)*(feature_data+2));
            break;
        default:
            break;
    }

    return ERROR_NONE;
}    /*    feature_control()  */

imgsensor_i2c_init 的实现
imgsensor_i2c_init() 在函数 imgsensor_set_driver() 中被调用。

enum IMGSENSOR_RETURN imgsensor_i2c_init(
    struct IMGSENSOR_I2C_CFG *pi2c_cfg,
    enum   IMGSENSOR_I2C_DEV  device)
{
    if (!pi2c_cfg ||
        device >= IMGSENSOR_I2C_DEV_MAX_NUM ||
        device < IMGSENSOR_I2C_DEV_0)
        return IMGSENSOR_RETURN_ERROR;

    pi2c_cfg->pinst       = &gi2c.inst[device];
    pi2c_cfg->pi2c_driver = &gi2c_driver[device];

    mutex_init(&pi2c_cfg->i2c_mutex);

    return IMGSENSOR_RETURN_SUCCESS;
}

imgsensor_hw_init 的实现
imgsensor_hw_init() 在函数 imgsensor_probe() 中被调用。

enum IMGSENSOR_RETURN imgsensor_hw_init(struct IMGSENSOR_HW *phw)
{
    struct IMGSENSOR_HW_SENSOR_POWER      *psensor_pwr;
    struct IMGSENSOR_HW_CFG               *pcust_pwr_cfg;
    struct IMGSENSOR_HW_CUSTOM_POWER_INFO *ppwr_info;
    int i, j;

    for (i = 0; i < IMGSENSOR_HW_ID_MAX_NUM; i++) {
        if (hw_open[i] != NULL)
            (hw_open[i])(&phw->pdev[i]);

        if (phw->pdev[i]->init != NULL)
            (phw->pdev[i]->init)(phw->pdev[i]->pinstance);
    }

    for (i = 0; i < IMGSENSOR_SENSOR_IDX_MAX_NUM; i++) {
        pcust_pwr_cfg = imgsensor_custom_config;
        while (pcust_pwr_cfg->sensor_idx != IMGSENSOR_SENSOR_IDX_NONE) {
            if (pcust_pwr_cfg->sensor_idx == i)
                break;
            pcust_pwr_cfg++;
        }

        if (pcust_pwr_cfg->sensor_idx == IMGSENSOR_SENSOR_IDX_NONE)
            continue;

        psensor_pwr = &phw->sensor_pwr[i];
        ppwr_info = pcust_pwr_cfg->pwr_info;
        while (ppwr_info->pin != IMGSENSOR_HW_PIN_NONE) {
            for (j = 0; j < IMGSENSOR_HW_ID_MAX_NUM; j++)
                if (ppwr_info->id == phw->pdev[j]->id)
                    break;

            psensor_pwr->id[ppwr_info->pin] = j;
            ppwr_info++;
        }
    }

    return IMGSENSOR_RETURN_SUCCESS;
}
 

imgsensor_i2c_create() 的实现


imgsensor_i2c_create() 在函数 imgsensor_probe() 中被调用。 在这个函数中注册了对应的 camera 驱动。

文件路径:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/common/v1/imgsensor_i2c.c

enum IMGSENSOR_RETURN imgsensor_i2c_create(void)
{
    int i;

    for (i = 0; i < IMGSENSOR_I2C_DEV_MAX_NUM; i++)
        i2c_add_driver(&gi2c_driver[i]);

    return IMGSENSOR_RETURN_SUCCESS;
}

IMGSENSOR_I2C_DEV_MAX_NUM 通过枚举进行赋值,其值为3,说明最多可以注册的摄像头驱动为3个。因为camera 驱动是挂载在 I2C总线 上,所以通过函数 i2c_add_driver() 进行注册。gi2c_driver 结构体对应一个具体 camera 设备的驱动。

gi2c_driver 结构体的实现
/* 如果使用了设备树,则CONFIG_OF会被定义 */
#ifdef CONFIG_OF
static const struct of_device_id gof_device_id_0[] = {
                    /* "mediatek,camera_main" */
    { .compatible = IMGSENSOR_I2C_OF_DRV_NAME_0, },
    {}
};
static const struct of_device_id gof_device_id_1[] = {
                    /* "mediatek,camera_sub" */
    { .compatible = IMGSENSOR_I2C_OF_DRV_NAME_1, },
    {}
};
static const struct of_device_id gof_device_id_2[] = {
                    /* "mediatek,camera_main_two" */
    { .compatible = IMGSENSOR_I2C_OF_DRV_NAME_2, },
    {}
};
#endif

static struct i2c_driver gi2c_driver[IMGSENSOR_I2C_DEV_MAX_NUM] = {
    {
        .probe = imgsensor_i2c_probe_0,
        .remove = imgsensor_i2c_remove,
        .driver = {
        .name = IMGSENSOR_I2C_DRV_NAME_0, // "kd_camera_hw"
        .owner = THIS_MODULE,
#ifdef CONFIG_OF
        .of_match_table = gof_device_id_0,
#endif
        },
        .id_table = gi2c_dev_id,
    },
    {
        .probe = imgsensor_i2c_probe_1,
        .remove = imgsensor_i2c_remove,
        .driver = {
        .name = IMGSENSOR_I2C_DRV_NAME_1, // "kd_camera_hw_bus2"
        .owner = THIS_MODULE,
#ifdef CONFIG_OF
        .of_match_table = gof_device_id_1,
#endif
        },
        .id_table = gi2c_dev_id,
    },
    {
        .probe = imgsensor_i2c_probe_2,
        .remove = imgsensor_i2c_remove,
        .driver = {
        .name = IMGSENSOR_I2C_DRV_NAME_2, // "kd_camera_hw_bus3"
        .owner = THIS_MODULE,
#ifdef CONFIG_OF
        .of_match_table = gof_device_id_2,
#endif
        },
        .id_table = gi2c_dev_id,
    }
}

上面匹配涉及到的匹配名称是在文件 imgsensor_cfg_table.h 中定义的,文件路径是:

./kernel-4.4/drivers/misc/mediatek/imgsensor/src/mt6739/camera_hw/imgsensor_cfg_table.h

/* 如果不使用设备树,会匹配这三个宏 */
#define IMGSENSOR_I2C_DRV_NAME_0  "kd_camera_hw"
#define IMGSENSOR_I2C_DRV_NAME_1  "kd_camera_hw_bus2"
#define IMGSENSOR_I2C_DRV_NAME_2  "kd_camera_hw_bus3"

/* 如果使用设备树,则会匹配这三个宏 */
#define IMGSENSOR_I2C_OF_DRV_NAME_0 "mediatek,camera_main"
#define IMGSENSOR_I2C_OF_DRV_NAME_1 "mediatek,camera_sub"
#define IMGSENSOR_I2C_OF_DRV_NAME_2 "mediatek,camera_main_two"

这些字符串会和设备树里的 compatible 进行匹配,匹配成功了会调用对应的 probe() 函数。

设备树中的配置


设备树里的 compatible 最开始是在哪里产生的呢?

答案: 通过 dws 工具匹配出来的,通过dws 工具修改,生成文件 k39tv1_bsp.dws 。

dws 工具路径:

​ ./vendor/mediatek/proprietary/scripts/dct/DrvGen.exe

dws 文件路径:

​ ./kernel-4.4/drivers/misc/mediatek/dws/mt6739/k39tv1_bsp.dws

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rUs1FTbU-1584518996982)(D:\libaojian\camera\picture\dws文件.png)]

通过 DrvGen.exe 这个工具可以修改 k39tv1_bsp.dws 文件中各参数的配置。

通过上述截图可以发现,主摄像头 CAMERA_MAIN 的I2C通讯地址是 0x37 , CAMERA_SUB 的 I2C 通讯地址是 0x3C ,对应生成的文件是 cust.dtsi。

cust.dtsi 中对camera的配置
文件路径:

./out/target/product/k39tv1_bsp/obj/KERNEL_OBJ/arch/arm/boot/dts/k39tv1_bsp/cust.dtsi

&i2c2 {
        #address-cells = <1>;
        #size-cells = <0>;
        clock-frequency = <400000>;
        mediatek,use-open-drain;
        camera_main_mtk:camera_main@37 {
                compatible = "mediatek,camera_main";
                reg = <0x37>;
                status = "okay";
        };

        camera_main_af_mtk:camera_main_af@0c {
                compatible = "mediatek,camera_main_af";
                reg = <0x0c>;
                status = "okay";
        };

        camera_sub_mtk:camera_sub@3c {
                compatible = "mediatek,camera_sub";
                reg = <0x3c>;
                status = "okay";
        };
};

当内核启动后,会解析dts编译生成的dtb文件,注册里面定义的 device ,代码中设置的 of_device_id 需要分别匹配上 cust.dtsi 文件中的 compatible 节点 “mediatek,camera_main” 和 “mediatek,camera_sub” 。如果和驱动中定义 compatible 字段一致,则挂载启动。上面注册了两个platform 驱动 gi2c_driver 。如果 compatible 匹配成功会调用各自的 probe 函数 imgsensor_i2c_probe_0,imgsensor_i2c_probe_1 。

k39tv1_bsp.dws 中对camera的配置
通过 k39tv1_bsp.dws 文件会生成对应的 DTS 文件 mt6739 和 k39tv1_bsp.dts

文件路径:

./kernel-4.4/arch/arm/boot/dts/mt6739.dts

kd_camera_hw1: kd_camera_hw1@15008000 {
    compatible = "mediatek,camera_hw";
    reg = <0 0x15008000 0 0x1000>;  /* SENINF_ADDR */
    /* Camera Common Clock Framework (CCF) */
    clocks = <&topckgen CLK_TOP_CAMTG_SEL>,
    <&topckgen CLK_TOP_CAMTG2_SEL>,
    <&clk26m>,
    <&topckgen CLK_TOP_UNIVPLL_48M_D2>,
    <&topckgen CLK_TOP_UNIVPLL2_D8>,
    <&topckgen CLK_TOP_UNIVPLL_D26>,
    <&topckgen CLK_TOP_UNIVPLL2_D32>,
    <&topckgen CLK_TOP_UNIVPLL_48M_D4>,
    <&topckgen CLK_TOP_UNIVPLL_48M_D8>,
    <&topckgen CLK_TOP_SENIF_SEL>,
    <&topckgen CLK_TOP_SCAM_SEL>;
    clock-names = "CLK_TOP_CAMTG_SEL",  
        "CLK_TOP_CAMTG2_SEL",
        "CLK_TOP_CLK26M",
        "CLK_TOP_UNIVPLL_48M_D2",
        "CLK_TOP_UNIVPLL2_D8",
        "CLK_TOP_UNIVPLL_D26",
        "CLK_TOP_UNIVPLL2_D32",
        "CLK_TOP_UNIVPLL_48M_D4",
        "CLK_TOP_UNIVPLL_48M_D8",
        "CLK_TOP_SENINF_SEL",
        "CLK_TOP_SCAM_SEL";
};

文件路径:

./kernel-4.4/arch/arm/boot/dts/k39tv1_bsp.dts

&pio {
        camera_pins_cam0_rst0: cam0@0 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO29__FUNC_GPIO29>;                                                     slew-rate = <1>; /*direction 0:in, 1:out*/
                 output-low;/*direction out used only. output_low or high*/                            };
        };
        camera_pins_cam0_rst1: cam0@1 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO29__FUNC_GPIO29>;
                slew-rate = <1>;
                output-high;
            };
        };
        camera_pins_cam0_pnd0: cam0@2 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO30__FUNC_GPIO30>;
                slew-rate = <1>;
                output-low;
            };
        };
        camera_pins_cam0_pnd1: cam0@3 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO30__FUNC_GPIO30>;
                slew-rate = <1>;
                output-high;
            };
        };
        camera_pins_cam1_rst0: cam1@0 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO22__FUNC_GPIO22>;
                slew-rate = <1>; /*direction 0:in, 1:out*/
                output-low;/*direction out used only. output_low or high*/
            };
        };
        camera_pins_cam1_rst1: cam1@1 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO22__FUNC_GPIO22>;
                slew-rate = <1>;
                output-high;
            };
        };
        camera_pins_cam1_pnd0: cam1@2 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO23__FUNC_GPIO23>;
                slew-rate = <1>;
                output-low;
            };
        };
        camera_pins_cam1_pnd1: cam1@3 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO23__FUNC_GPIO23>;
                slew-rate = <1>;
                output-high;
            };
        };
        camera_pins_cam_ldo_sub_vcamd_0: cam1@vcamd0 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO9__FUNC_GPIO9>;
                slew-rate = <1>;
                output-low;
            };
        };
        camera_pins_cam_ldo_sub_vcamd_1: cam1@vcamd1 {
            pins_cmd_dat {
                pins = <PINMUX_GPIO9__FUNC_GPIO9>;
                slew-rate = <1>;
                output-high;
            };
        };
        camera_pins_cam0_mclk_on: camera_pins_cam0_mclk_on {
            pins_cmd_dat {
                pins = <PINMUX_GPIO126__FUNC_CMMCLK0>;
            };
        };
        camera_pins_cam0_mclk_off: camera_pins_cam0_mclk_off {
            pins_cmd_dat {
                pins = <PINMUX_GPIO126__FUNC_GPIO126>;
            };
        };
        camera_pins_cam1_mclk_on: camera_pins_cam1_mclk_on {
            pins_cmd_dat {
                pins = <PINMUX_GPIO126__FUNC_CMMCLK0>;
            };
        };
        camera_pins_cam1_mclk_off: camera_pins_cam1_mclk_off {
            pins_cmd_dat {
                pins = <PINMUX_GPIO126__FUNC_GPIO126>;
            };
        };
        camera_pins_default: camdefault {
        };
};

&kd_camera_hw1 {
        pinctrl-names = "default",
                        "cam0_rst0", "cam0_rst1", "cam0_pnd0", "cam0_pnd1",
                        "cam1_rst0", "cam1_rst1", "cam1_pnd0", "cam1_pnd1",
                        "cam_ldo_sub_vcamd_0", "cam_ldo_sub_vcamd_1",
                        "cam0_mclk_on", "cam0_mclk_off",
                        "cam1_mclk_on", "cam1_mclk_off";
        pinctrl-0 = <&camera_pins_default>;
        pinctrl-1 = <&camera_pins_cam0_rst0>;
        pinctrl-2 = <&camera_pins_cam0_rst1>;
        pinctrl-3 = <&camera_pins_cam0_pnd0>;
        pinctrl-4 = <&camera_pins_cam0_pnd1>;
        pinctrl-5 = <&camera_pins_cam1_rst0>;
        pinctrl-6 = <&camera_pins_cam1_rst1>;
        pinctrl-7 = <&camera_pins_cam1_pnd0>;
        pinctrl-8 = <&camera_pins_cam1_pnd1>;
        pinctrl-9 = <&camera_pins_cam_ldo_sub_vcamd_0>;
        pinctrl-10 = <&camera_pins_cam_ldo_sub_vcamd_1>;
        pinctrl-11 = <&camera_pins_cam0_mclk_on>;
        pinctrl-12 = <&camera_pins_cam0_mclk_off>;
        pinctrl-13 = <&camera_pins_cam1_mclk_on>;
        pinctrl-14 = <&camera_pins_cam1_mclk_off>;
        status = "okay";
};

由上面代码可以发现有两个 kd_camera_hw1 ,这里遵循一种规则:

如果第一个 kd_camera_hw1 中定义的属性在第二个 kd_camera_hw1 中也有定义则使用第二个 kd_camera_hw1 中定义的属性;
如果第一个 kd_camera_hw1 中定义的属性在第二个 kd_camera_hw1 中没有定义,则使用第一个 kd_camera_hw1 中定义的属性;
camera驱动文件的执行流程
主要执行 kernel-4.4\drivers\misc\mediatek\imgsensor\src\common\v1 目录下的 imgsensor.c --> imgsensor_sensor_list.c 获取 camera 设备信息。 执行 imgsensor.c --> imgsensor_hw.c --> imgsensor_cfg_table.c 获取平台的上电信息。

总结
通过上述分析,我们可以看出, camera 驱动先是注册 platform 平台驱动,再注册 I2c 驱动,然后又为前后摄注册字符设备,封装底层方法 imgsensor_ioctl,上层访问底层驱动时候先是使用 setdriver 获取具体IC的驱动的入口,然后使用 checkisalive 对 sensorlist 中的 IC 进行上电,上电完成就通过 I2C 读取设备 ID ,到此为止,上层应用与底层驱动挂接完成,紧接着就是预览和拍照,不过这都是具体 IC 驱动的实现了。
 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MT6739的Android9.0 Camera kernel 驱动 的相关文章

  • rabbitmq 安装

    文章目录 RabbitMQ 安装 erlang安装 RabbitMQ安装 环境变量 管理界面 添加用户 配置允许远程访问的用户 未验证 RabbitMQ 安装 erlang安装 http www erlang org downloads R
  • 鼠标、键盘、窗口监听事件

    一 画笔paint 画笔 public class TestPaint public static void main String args new MyPaint loadFrame class MyPaint extends Fram
  • openwrt路由器(RP-LINK)安装python并设置开机启动程序

    由于项目需求 实际条件限制 需要在某台设备上运行一个python小程序 在工业机器人和云服务器之间实现信息转发的功能 因为机器人也需要通过路由器认证连接校园网 出于简化设备的考虑 不想每次跑程序还得开电脑 我决定尝试在路由器上运行这个程序
  • js时间戳与日期格式的转换

    1 将时间戳转换成日期格式 function timestampToTime timestamp 时间戳为10位需 1000 时间戳为13位不需乘1000 var date new Date timestamp 1000 var Y dat
  • 软件测试用例设计之因果图

    软件测试用例设计之因果图 自动贩卖机功能测试 若投入5角钱或1元钱的硬币 押下 橙汁 或 啤酒 的按钮 则相应的饮料就送出来 若售货机没有零钱找 则一个显示 零钱找完 的红灯亮 这时在投入1元硬币并押下按钮后 饮料不送出来而且1元硬币也退出
  • char* buf和char buf[64],定义两种字符串作为参数传递给函数的区别

    问题描述 最近在项目中遇到了一个让人疑惑的问题 有一个发送函数需要传递void 类型的参数 我定义了char buf abcdefg 然后将buf作为参数传递给了这个发送函数 但是函数返回值显示发送成功 但是另一端没有接收到数据 查找了好久
  • WEB服务器和应用服务器有什么区别

    author skate time 2009 12 04 俗的讲 Web服务器传送 serves 页面使浏览器可以浏览 然而应用程序服务器提供的是客户端应用程序可以调用 call 的方法 methods 确切一点 你可以说 Web服务器专门
  • 部署MES管理系统首先要解决什么问题

    随着制造业市场竞争的加剧 企业需要更加高效 灵活的生产运营 以提高产品质量和降低成本 在这种情况下 MES管理系统解决方案成为许多企业的选择 然而 在部署MES管理系统之前 必须首先解决一些关键问题 以确保系统的成功实施 本文将探讨部署ME
  • C和C++中的结构体

    解释一 C C 结构体的区别 C中的结构体和C 中结构体的不同之处 在C中的结构体只能自定义数据类型 结构体中不允许有函数 而C 中的结构体可以加入成员函数 C 中的结构体和类的异同 一 相同之处 结构体中可以包含函数 也可以定义publi
  • 基于Rancher实现kubernetes集群管理

    基于Rancher实现kubernetes集群管理 1 Rancher介绍 2 Rancher部署 添加kubernetes集群 3 Rancher简单操作 1 Rancher介绍 Rancher可以通过图形化操作的方式管理kubernet
  • JAVA利用HttpClient进行POST请求(HTTPS)

    目前 要为另一个项目提供接口 接口是用HTTP URL实现的 最初的想法是另一个项目用JQuery post进行请求 但是 很可能另一个项目是部署在别的机器上 那么就存在跨域问题 而JQuery的post请求是不允许跨域的 这时 就只能够用
  • 逆水寒跑商时服务器维护,逆水寒跑商路线推荐 合适的路线让你事半功倍

    逆水寒跑商路线推荐 帮会跑商可以赚到大量的财富 因为跑商比较浪费时间也比较危险 所以我们需要研究一条比较合理的路线 这样对于各位镖师来说才是最省时省力的方法 汴京 菊花酒 杭州 贸易信息 木板年画 宋辽边境 磁州 3000以下 贸易信息赚更
  • Linux学习笔记(九) -- 利用Code::Blocks建立C++静态链接库

    1 测试平台 测试平台 Linux版本 Ubuntu 18 04 LTS Code Blocks版本 16 01 2 操作步骤 2 1 启动Code Blocks 2 2 新建静态链接库工程 1 选择 File 菜单中的 New Proje
  • 总结:nn.Module的children()与modules()方法、如何获取网络的某些层

    一 nn Module的children 方法与modules 方法的区别 children 与modules 都是返回网络模型里的组成元素 但是children 返回的是最外层的元素 modules 返回的是所有的元素 包括不同级别的子元
  • osg学习(三十九)DisplaySettings

    DisplaySettings是osg的全局单实例变量 类似osg的Registry 主要记录窗口的一些显示设置 比如窗口尺寸 多重纹理采样数 着色器设置等 osg在创建窗口时会从该变量中读取信息 这个变量中的信息可以通过命令行输入也可以通
  • 2023Matlab初级教程- 第二章 Matlab 基础运算及函数、数据类型

    第二章 Matlab 基础运算及函数 数据类型 文章目录 第二章 Matlab 基础运算及函数 数据类型 前言 一 基础运算 二 基础函数 1 基础函数 2 练习 三 数据类型 3 1特殊变量 3 2调用优先级 3 3 1数组 3 3 2结
  • 转码日记——Javascript笔记(4)

    对象 引用数据类型 基本数据类型的不足 基本数据类型都是单一的值 值和值之间没有任何联系 如果只使用基本数据类型 那么创建的变量都是独立的个体 不能成为一个整体 对象属于复合的数据类型 在对象中可以保存多个不同数据类型的属性 分类 1 内建
  • 21份软件测试全流程文档模板(标准版)

    1 需求说明书 2 功能测试计划 3 功能测试用例 4 业务流程测试用例 5 系统安装配置说明书 6 阶段功能测试报告 7 性能测试计划 8 性能测试用例 9 性能测试报告 10 系统功能测试报告 11 需求变更说明书 12 用户建议说明书
  • VS中C语言调Fortran,急急急!哪位大哥能帮我一下,把以下FORTRAN语言的程序转换成C语言...

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 PROGRAM MAIN IMPLICIT NONE INTEGER I NUM NMAX M MB ME MS PN PARAMETER NMAX 2000 parameter PN 1500

随机推荐

  • Nand flash

    一 需要什么 我们的目的是编写Nand Flash 的裸机代码 可以读写Nand Flash 那么我们应该从哪开始呢 以我为例 我板子上的cpu 是时s5pv210 板子上的nand flash的芯片是1Gbyte 的 NAND FLASH
  • Idea如何导入一个SpringBoot项目

    最近公司要求开发工具要用Idea 作为一个eclipse的老员工 记录一下Idea中遇到的坑 刚开始用Idea从Git上导入一个项目时 遇到了很多坑 网上有很多方法 我不多做介绍 只说明一下我使用的方法 1 本地新建一个文件夹 从git上导
  • email.class.php,利用PHP发送邮件Class类

    记录一下 function send code email admin kieng cn title 标题 message 内容 toemail email 定义收件人的邮箱 sendmail xxxx 163 com 发件人邮箱 send
  • C# 子类如何访问子类的方法(同一父类)

    在继承关系中 子类可以通过创建另一个子类的对象来访问其方法 下面是一个示例 展示了子类如何访问另一个子类的方法 public class Animal public virtual void Speak Console WriteLine
  • arm push/pop/b/bl汇编指令

    目录 1 push指令 2 pop指令 3 b指令 4 bl指令 5 bx指令 1 push指令 功能描述 入栈 armv7 芯片手册 Push Multiple Registers stores multiple registers to
  • day3作业

    思维导图 第2题 第3题
  • got an unexpected keyword argument ‘datasets‘的解决方法

    发生异常 TypeError note full exception trace is shown but execution is paused at init got an unexpected keyword argument dat
  • Linux下vim的常见命令操作(快速复查)

    目录 前言 1 Vim常用操作 1 1 环境参数 1 2 方向 1 3 插入命令 1 4 定位命令 1 5 删除命令 1 6 复制和剪切命令 1 7 替换和取消命令 1 8 搜索和搜索替换命令 1 9 保存和退出命令 1 10 其他命令 1
  • OpenAI不能访问有什么方法解救呢?试试这方法吧

    最近发现国内不挂代理是不能访问到openAI的接口的 为了解决这个问题 我一直在github上需在解决方案 今天终于被我找到一个大神开源了一个解决方案 下面就来看看如何做吧 整个项目的代码很简单只有几行代码 rewrites source
  • chatgpt赋能Python-pythonmul

    Pythonmul 让Python更加高效的优化工具 Python是一种被广泛应用于数据分析 科学计算 人工智能等各个领域的高级编程语言 由于其简单易学 灵活多样的编程风格以及庞大的社区支持 Python成为了许多开发者的首选语言 但是 P
  • https网站打不开怎么办?解决方法看这里

    https网站 即安装了SSL证书的网站 打不开的情况也会经常出现 不论是什么网站 只要长时间打不开就会影响到用户体验度和网站本身的流量情况 对于网站的优化也是非常不利的 如果出现了这种情况该怎么解决呢 https网站打不开可能是由多种原因
  • js 保留6位有效数字,直接舍去,不四舍五入

    function sixNum num return Math floor num 1000000 1000000 sixNum 12 123456789 12 123456
  • PCB阻焊层太近了会不会有问题?

    绘制pcb双层板 进行DCR检查 发现如下报错 于是回到pcb的界面去查看 原来是我的组焊层靠的很近 小于规则的6mil 这个报错有必要修改嘛 规则的设置如下 最小组焊层裂口是6mil 但是封装就是官网上下载下来的 是芯片封装引脚的问题 过
  • AttemptID:attempt_1557891872692_0001_r_000000_0 Timed out after 3600 secs

    背景 做kylin 的时候 执行了 hive的命令 是hive数据的重新分布 结果在reduce的时候阻塞了 查看原因为 AttemptID attempt 1557891872692 0001 r 000000 0 Timed out a
  • 公有云、私有云、混合云

    云的部署方式有很多种 如公有云 私有云 混合云等 部署在云上的SaaS主要分为公有云SaaS和私有云SaaS 行业主流的SaaS部署模式是公有云SaaS 私有云部署模式 适用于某些有特殊要求的行业和企业业务 要求有较大的私有化和定制化空间的
  • python case when用法_SQL之CASE WHEN用法详解

    简单CASE WHEN函数 CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE
  • angular蚂蚁_angular4 调用api

    angular2 问题请教 angular2 通过http服务进行对后端api的远程调用 我简单的尝试了一下 发现了几个问题 记录一下 以方便查找问题 angular2 http服务的跨域问题 跨域本身就是一个很复杂的问题 angular2
  • 剑指 Offer 27. 二叉树的镜像 -- 递归

    0 题目描述 leetcode原题链接 剑指 Offer 27 二叉树的镜像 1 递归算法 根据二叉树镜像的定义 考虑递归遍历 d f s mathrm dfs dfs 二叉树 交换每个节点的左 右子节点 即可生成 二叉树的镜像 递归解析
  • Qt入门-文本框类QLineEdit和QTextEdit

    QLineEdit是单行文本框 QTextEdit是多行文本框 1 单行文本框QLineEdit 常用的方法和属性 a 获取和设置文本对齐方式 Qt Alignment alignment const void setAlignment Q
  • MT6739的Android9.0 Camera kernel 驱动

    文章目录 Kernel 层驱动的实现 Camera 开机流程 Camera 驱动的文件结构 Camera 驱动初始化流程 Camera 入口函数 imgsensor init 注册的平台驱动结构体 gimgsensor platform d