MTK camera驱动浅析(1)

目录

一.主要文件

二.camera框架中的结构

三.camera驱动初始化流程


一.主要文件

         1.文件路径         

        kernel-4.19/drivers/misc/mediatek/imgsensor/src/common/v1/

         2.文件内容介绍

 

二.camera框架中的结构

         整个框架的关键结构体是IMGSENSOR,可以看到它包含了IMGSENSOR_HW(上电相关)结构体和IMGSENSOR_SENSOR sensor[ ]结构体数组等一些成员,当前项目使用到的cmos sensor(图像传感器,后面都称呼为sensor)的各种信息就会随着这个驱动框架的启动在这个数组中完成初始化以及调用,如果项目中有一个前摄像头一个后摄像头,这个结构体数组中就会有两个元素将它们分别保存起来。具体结构如下:

struct IMGSENSOR {
    struct IMGSENSOR_STATUS status;
    struct IMGSENSOR_HW hw;//上电用到
    struct IMGSENSOR_CLK clk;
    struct IMGSENSOR_SENSOR sensor[IMGSENSOR_SENSOR_IDX_MAX_NUM];//操作sensor用到
    /* 省略部分成员 */
};

三.camera驱动初始化流程

         入口函数注册一个platform驱动:      

static int __init imgsensor_init(void) {
    if (platform_driver_register(&gimgsensor_platform_driver)) {
        PK_DBG("failed to register CAMERA_HW driver\n");
        return -ENODEV;
    }
    /* ...省略部分代码... */
    return 0;
}

         platform驱动结构体如下:

#ifdef CONFIG_OF
static const struct of_device_id gimgsensor_of_device_id[] = {
        {.compatible = "mediatek,imgsensor",},    //与设备树进行匹配
        {}
};
#endif

static struct platform_driver gimgsensor_platform_driver = {
        .probe   = imgsensor_probe,    //匹配成功调用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,
#endif
        }
};

         与设备树匹配成功之后会调用probe()函数:

static int imgsensor_probe(struct platform_device *pdev) {
    /* 注册字符设备 */
    if (imgsensor_driver_register()) {
        PK_DBG("[CAMERA_HW] register char device failed!\n");
        return -1;
    }
    /* ...省略部分代码... */
    /* 上电初始化 */
    imgsensor_hw_init(&pgimgsensor->hw);
    /* 注册i2c driver */
    imgsensor_i2c_create();
    /* proc文件系统初始化  */
    imgsensor_proc_init();
    /* ...省略部分代码... */
    return 0;
}

         probe()函数主要做了创建字符设备、上电信息初始化、添加i2c设备等工作。其中的注册字符设备时添加了文件操作集:

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
};

         这里可以看到camera设备驱动使用ioctl来控制,imgsensor_ioctl()函数实现如下:

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

    if (_IOC_DIR(a_u4Command) != _IOC_NONE) {
        pBuff = kmalloc(_IOC_SIZE(a_u4Command), GFP_KERNEL);
        /* ...省略部分代码... */
        if (_IOC_WRITE & _IOC_DIR(a_u4Command)) {
            if (copy_from_user(pBuff, (void *) a_u4Param, _IOC_SIZE(a_u4Command)))
            /* ...省略部分代码... */
        } else
            memset(pBuff, 0, _IOC_SIZE(a_u4Command));
    } 
    /* ...省略部分代码... */
        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,
                        (struct ACDK_SENSOR_MCLK_STRUCT *) pBuff);
                break;
                /* ...省略部分代码... */
        }
    /* ...省略部分代码... */
    return i4RetValue;
}

         这里除了获取一些信息之外,有一个adopt_CAMERA_HW_FeatureControl()函数实现通过ioctl来控制硬件:

static inline int adopt_CAMERA_HW_FeatureControl(void *pBuf) {
    struct ACDK_SENSOR_FEATURECONTROL_STRUCT *pFeatureCtrl;
    struct IMGSENSOR_SENSOR *psensor;
    unsigned int FeatureParaLen = 0;
    void *pFeaturePara = NULL;
    struct ACDK_KD_SENSOR_SYNC_STRUCT *pSensorSyncInfo = NULL;
    signed int ret = 0;
    pFeatureCtrl = (struct ACDK_SENSOR_FEATURECONTROL_STRUCT *) pBuf;
    /* InvokeCamera与sensor枚举对应,例如0代表前摄,1代表后摄 */
    psensor = imgsensor_sensor_get_inst(pFeatureCtrl->InvokeCamera);

    if (copy_from_user((void *) &FeatureParaLen,
                       (void *) pFeatureCtrl->pFeatureParaLen,
                       sizeof(unsigned int))) {

        PK_DBG(" ioctl copy from user failed\n");
        return -EFAULT;
    }
    /* ...省略部分代码... */
    pFeaturePara = kmalloc(FeatureParaLen, GFP_KERNEL);

    memset(pFeaturePara, 0x0, FeatureParaLen);

    /* copy from user */
    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;
        }
        case SENSOR_FEATURE_CHECK_IS_ALIVE:
            imgsensor_check_is_alive(psensor);
            break;
            /* ...省略部分代码... */
            imgsensor_sensor_feature_control(
                    psensor,
                    SENSOR_FEATURE_SET_ESHUTTER,
                    (unsigned char *) &pSensorSyncInfo->u2SensorNewExpTime,
                    (unsigned int *) &FeatureParaLen);

            imgsensor_sensor_feature_control(
                    psensor,
                    SENSOR_FEATURE_SET_GAIN,
                    (unsigned char *) &pSensorSyncInfo->u2SensorNewGain,
                    (unsigned int *) &FeatureParaLen);
            break;

            return ret;
    }
}

         imgsensor_sensor_get_inst()这个函数传入的是ioctl的参数,这个参数直接作为数组索引,拿到了文章开头的struct IMGSENSOR_SENSOR sensor[ ]数组中的一个元素地址,准备开始初始化这个ioctl参数所代表的sensor:

struct IMGSENSOR_SENSOR *imgsensor_sensor_get_inst(enum IMGSENSOR_SENSOR_IDX idx) {
    if (idx < IMGSENSOR_SENSOR_IDX_MIN_NUM ||
        idx >= IMGSENSOR_SENSOR_IDX_MAX_NUM)
        return NULL;
    else
        return &pgimgsensor->sensor[idx];
}

         之后会调用imgsensor_set_driver()函数用来设定某一个具体的sensor:

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

    struct IMGSENSOR_SENSOR_INST *psensor_inst = &psensor->inst;
    /* kdSensorList[]数组包含具体sensor的驱动信息 */
    struct IMGSENSOR_INIT_FUNC_LIST *pSensorList = kdSensorList;
#define TOSTRING(value)           #value
#define STRINGIZE(stringizedName) TOSTRING(stringizedName)
/*
 * 在配置文件xxx_defconfig中申明可使用的sensor名字,
 * 例如当前项目的配置:
 * CONFIG_CUSTOM_KERNEL_IMGSENSOR="ovxxxx_qtech_main_mipi_raw gcxxxx_kingc_front_mipi_raw
 *                         s5k4xxxx_kingc_main_mipi_raw ovxxxx_ofilm_front_mipi_raw"
 * 这就表示当前项目支持ovxxxx、gcxxxx、三星xxx等这四个摄像头
 */
    char *psensor_list_config = NULL, *psensor_list = NULL;
    char *sensor_configs = 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;

    imgsensor_mutex_init(psensor_inst);
    /* 绑定i2c */
    imgsensor_i2c_init(&psensor_inst->i2c_cfg,
                       imgsensor_custom_config[
                               (unsigned int) psensor_inst->sensor_idx].i2c_dev);
    imgsensor_i2c_filter_msg(&psensor_inst->i2c_cfg, true);
    /* 配置文件和kdSensorList[]数组中的信息匹配 */
    if (get_search_list) {
        psensor_list = psensor_list_config =
                kmalloc(strlen(sensor_configs) - 1, GFP_KERNEL);

        if (psensor_list_config) {
            for (j = 0; j < MAX_NUM_OF_SUPPORT_SENSOR; j++)
                orderedSearchList[j] = -1;

            memcpy(psensor_list_config,
                   sensor_configs + 1,
                   strlen(sensor_configs) - 2);

            *(psensor_list_config + strlen(sensor_configs) - 2) = '\0';

            PK_DBG("sensor_list %s\n", psensor_list_config);
            driver_name = strsep(&psensor_list_config, " \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)) {
                        orderedSearchList[i++] = j;
                        break;
                    }
                }
                driver_name =
                        strsep(&psensor_list_config, " \0");
            }
            get_search_list = false;
        }
        kfree(psensor_list);
    }
    
    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) {
            /* 匹配完成之后调用具体的某个sensor的初始化函数 */
            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_DBG(
                            "[%s]:[%d][%d][%s]\n",
                            __func__,
                            psensor->inst.sensor_idx,
                            drv_idx,
                            psensor_inst->psensor_name);

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

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

    return ret;
}

         具体分析这段代码,发现它主要做了这样一些工作:

  1.  根据ioctl参数初始化i2c设备
  2. 检查当前项目支持哪些sensor
  3. 初始化ioctl参数代表的这个sensor,绑定sensor的操作函数
  4. 执行一次上下电操作来试验一下这个sensor。

         其中,检查当前支持的sensor过程如下:

         ①申明一个数组orderedSearchList[MAX_NUM_OF_SUPPORT_SENSOR],从命名来看是“有序地搜索”,长度是最大支持的sensor,用来保存当前项目支持的sensor信息。

         ②把配置文件中保存的sensor名字取出来保存到psensor_list_config。

         ③用strsep()函数来分解psensor_list_config,结果用driver_name保存。

         ④把driver_name和pSensorList[]中保存的sensor的name相比对,如果名字对应,就把该sensor在pSensorList[]中的索引值保存到orderedSearchList[]数组。

         匹配过程如下图所示:


 

         为sensor绑定操作函数,通过调用sensor_func就可以实现对sensor的操作:

static struct SENSOR_FUNCTION_STRUCT sensor_func = {
        open,//初始化和注册
        get_info,
        get_resolution,
        feature_control,//对sensor工作模式等进行设置
        control,
        close
};

/* init */
UINT32 OV8856_QTECH_MAIN_MIPI_RAW_SensorInit(struct SENSOR_FUNCTION_STRUCT **pfFunc) {
    if (pfFunc != NULL)
        *pfFunc = &sensor_func;
    return ERROR_NONE;
}

         使用imgsensor_check_is_alive()函数来试验sensor,也就是执行上下电操作:

static inline int imgsensor_check_is_alive(struct IMGSENSOR_SENSOR *psensor) {
    struct IMGSENSOR_SENSOR_INST *psensor_inst = &psensor->inst;
    UINT32 err = 0;
    MUINT32 sensorID = 0;
    MUINT32 retLen = sizeof(MUINT32);

    IMGSENSOR_PROFILE_INIT(&psensor_inst->profile_time);
    /* 上电 */
    err = imgsensor_hw_power(&pgimgsensor->hw,
                             psensor,
                             psensor_inst->psensor_name,
                             IMGSENSOR_HW_POWER_STATUS_ON);
    /* 检查sensor id */
    if (err == IMGSENSOR_RETURN_SUCCESS)
        imgsensor_sensor_feature_control(
                psensor,
                SENSOR_FEATURE_CHECK_SENSOR_ID,
                (MUINT8 * ) & sensorID,
                &retLen);

    if (sensorID == 0 || sensorID == 0xFFFFFFFF) {
        PK_DBG("Fail to get sensor ID %x\n", sensorID);
        err = ERROR_SENSOR_CONNECT_FAIL;
    } else {
        PK_DBG(" Sensor found ID = 0x%x\n", sensorID);
        err = ERROR_NONE;
    }

    if (err != ERROR_NONE)
        PK_DBG("ERROR: No imgsensor alive\n");
    /* 下电 */
    imgsensor_hw_power(&pgimgsensor->hw,
                       psensor,
                       psensor_inst->psensor_name,
                       IMGSENSOR_HW_POWER_STATUS_OFF);

    IMGSENSOR_PROFILE(&psensor_inst->profile_time, "CheckIsAlive");

    return err ? -EIO : err;
}

         具体上电过程后续分析。(未完待续)

         本人还在入门阶段,欢迎大家指出错误。

参考巨佬博客:karaskass的博客_CSDN博客-camera driver+hal+framework,《Android深度探索 卷1 HAL与驱动开发》笔记,audio driver+hal+framework领域博主

                  

  • 7
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: MTK平台上的camera驱动教学课程旨在教授学员有关MTK平台上如何编写、安装和调试camera驱动程序的知识和技能。在这门课程中,学员将学习如何使用MTK平台提供的相应工具和接口来开发和优化camera驱动,以实现更好的图像质量和性能。 课程的内容包括以下几个方面: 1. 理论知识:学员将了解摄像头驱动的基本原理和工作原理,包括硬件架构、设备驱动接口和底层算法等。 2. 驱动开发环境搭建:学员将学习如何在MTK平台上搭建合适的开发环境,包括操作系统、编译器和调试工具等。 3. 驱动编写:学员将学习如何编写MTK平台上的camera驱动程序,包括设备初始化、图像采集和处理等功能的实现。 4. 驱动安装与调试:学员将学习如何将开发好的驱动程序安装到MTK平台上,并进行调试和优化,以确保驱动的稳定性和性能。 5. 实践项目:学员将完成一个实际的项目,如编写一个简单的camera应用程序或实现某种特定的图像处理算法,以应用所学知识并提升实践能力。 通过这门课程,学员将掌握MTK平台上camera驱动开发的基本技能,能够独立完成camera驱动的编写、安装和调试工作,并具备进一步深入研究和优化的能力。这对于从事手机软件开发、图像处理等领域的工程师和研究人员来说,将是一门非常实用和重要的课程。 ### 回答2: MTK平台的Camera驱动教学课程是针对使用MTK芯片的开发人员设计的一门课程。该课程旨在教授学员有关MTK平台中Camera驱动的基础知识、开发技巧和调试方法。 首先,课程将介绍MTK平台和其特点,帮助学员了解MTK芯片及其应用领域。接下来,课程将重点讲解Camera驱动的原理和工作机制,涵盖Camera传感器的工作原理、图像采集和处理流程等方面的知识。 课程还将介绍Camera驱动开发环境和工具,如MTK平台上的Camera HAL层和相应的调试工具。学员将学会如何配置和编译Camera驱动,以及如何使用调试工具进行驱动的调试和性能优化。 除了理论知识的讲解,课程还将通过实例演示和实践操作帮助学员掌握Camera驱动开发技巧。学员将有机会参与实际的Camera驱动开发项目,通过实践来加深对课程知识的理解和应用能力。 总结起来,MTK平台的Camera驱动教学课程旨在提供一套系统化的学习资源,帮助学员全面了解和掌握MTK平台上的Camera驱动开发。通过学习这门课程,学员将能够独立进行MTK平台上Camera驱动开发和调试工作,从而为MTK芯片应用的开发和优化提供支持。 ### 回答3: MTK平台CMOS摄像头驱动开发是一门与软硬件结合紧密的技术课程。首先,学习者需要熟悉MTK平台的基本知识,包括芯片结构、嵌入式系统以及Linux操作系统的基本原理。其次,学习者需要了解摄像头工作原理和常用的摄像头传感器类型,例如CMOS和CCD传感器。在掌握这些基础知识后,学习者可以开始学习MTK平台上的摄像头驱动开发。 在开发摄像头驱动时,学习者需要掌握如何通过MTK平台提供的驱动接口与摄像头硬件进行通信。这包括配置摄像头的分辨率、帧率、曝光时间以及白平衡等参数。此外,还需要了解如何处理摄像头的数据传输和处理,例如图像的压缩、旋转、镜像等操作。 在学习过程中,学习者可以通过阅读相关的文档和资料,参考MTK平台提供的示例代码来加深理解。同时,实际动手开发和调试也是非常重要的,可以通过连接MTK平台开发板和摄像头来调试和验证自己的驱动代码。学习者还可以通过与其他开发者交流和分享经验,提升自己的开发能力。 总之,MTK平台摄像头驱动开发是一门需要结合软硬件知识的综合性课程,通过学习掌握相关的理论知识和实践经验,可以帮助学习者深入了解摄像头驱动的工作原理和开发流程,提高自己的技术水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值