【Android Camera】Camera笔记

高通新的camera驱动架构设计发生了一些变化,借用互联网上常用的一种结构,大致的原理如此:将camera的所有功能划分为不同的模块,让模块自己来决定自己的事情(高内聚,低耦合),模块需要有统一的接口和格式。模块中有端口,通过端口把模块连接起来,又把模块挂在总线上。每一个端口的连接就是一个流,把这些流用pipeline来管理。每次启动一个camera就创建一个会话,由这个会话来管理此camera的一切事物。对于每一个会话,模块是共享的,它可以是camera的硬件资源也可以是其它资源(如一些软件算法等资源)。

那么如何来定义这个模块的结构呢?
1.端口——端口属于模块,如果这个模块只有source端口,那么它就是一个src模块;如果只有sink端口就是sink模块,如果都有就是中间模块。没有端口的模块是不能连接到流中的,但他可以完成一些其他的功能,比如接收引擎的设置,报告事件到bus等。连接到流中的端口,也就是说流事件(set/get)主要通过端口来处理。而来自于引擎的(set/get)通过模块来处理,当然端口也可以把事件交给模块来处理。模块内部的端口可以通过模块来建立关系,也可以建立内部的连接,端口有关get/set process。
2.模块线程——每个模块可以有一个线程来处理模块的事情。一个线程对应一个队列,线程就是从队列中取出数据处理,然后应答回去。
3.总线回调——挡一个模块向总线注册时,总线向其提供一个回调函数,当模块有事件发生时,调用这个函数向bus发消息,然后总线把这个消息提交给管道,管道把这个消息顺着流发下去。
4.模块的get、set以及process。

 

管道、引擎与会话
管道有两端,一端用于读,一端用于写。camera引擎负责对管道的监控,而会话管理camera引擎。

 

从代码结构上来看这种新的驱动架构
高通的camera deamon代码放置在vendor\qcom\proprietary\mm-camera目录下,而此目录下的mm-camera2就是新的camera架构位置,进入里面可以看到media-controller、server-imaging、server-tuning及其它几个目录,我们这里需要关注的就是media-controller目录。

media-controller
    |- mct——应该就是camera的引擎?里面包含了引擎、pipiline、bus、module、stream及event等定义及封装。
    |- modules——这里面就是划分好的一些模块代码,各模块大致功能如下
        |- sensor —— sensor 的驱动模块?                                                                              
        |- iface —— ISP interface模块                                                                                        
        |- isp —— 主要是ISP的处理,其内部又包含了众多的模块                                                
        |- stats —— 一些统计算法模块,如3A,ASD,AFD,IS,GRRO等数据统计的处理                          
        |- pproc —— post process处理                                                                                 
        |- imglib —— 主要是图片的一些后端处理,如HDR等                                  

以上各模块内部又包含了众多的模块,具体需要看代码分析。

高通camera daemon进程


1.概述
高通在Android的camera架构中,依旧沿用了其传统的方式,将其自身的一些处理放在一个daemon进程中。这部分内容出于应用于driver之间,是为了保护自身及硬件厂商的利益而专门弄出来的一个东东。其它各家平台也采用类似的方式将这部分的处理放在HAL层处理。

 

2.进程的入口
    做为一个单独的进程,那肯定是有其main函数入口的。在vendor\qcom\proprietary\mm-camera\mm-camera2\server-imaging\server.c文件中可以看到这个main函数。在这个函数中主要做了以下几件事情。
    1.找到服务节点的名字并打开此节点
        get_server_node_name(serv_hal_node_name)
        ......
        hal_fd->fd[0] = open(dev_name, O_RDWR | O_NONBLOCK); //这里dev_name为节点名如"/dev/serv_hal_node_name"
    2.初始化模块。目前有sensor、iface、isp、stats、pproc及imglib六个模块(见笔记一)
        server_process_module_init();
    3.进入主循环来处理来自HAL及MCT的事件及消息,处理完之后的结果反馈给kernel(msm.c)
        RD_FD_HAL 
            ----> server_process_hal_event(&event)。此函数如果返回真,表示事件消息已经传给了MCT,这时不需要发送CMD ACK给kernel,因为MCT处理结束后会发出通知。如果返回假,表示没有传到MCT,此时需要立即发送CMD ACK到kernel,以便HAL发送此消息的线程阻塞住。
        RD_DS_FD_HAL —— 通过domain socket传自HAL的消息
            ----> server_process_hal_ds_packet(fd_info->fd
        RD_PIPE_FD_MCT —— 来自media controller的消息

media controller线程

1.概述
    MCT线程是camera新架构的引擎部分,负责对管道的监控,由此来完成一个camera设备的控制运转。它运行在daemon进程空间,由MSM_CAMERA_NEW_SESSION事件来开启,具体开启函数为mct_controller_new()。

2.mct_controller_new()函数
    此函数创建一个新的MCT引擎,这将对应一个事务的pipeline。我们知道上层可以创建多个事务,每个对应一个camera,也对应自己的MCT及pipeline等。因此这个函数的主要完成以下几件事情:
    1.mct_pipeline_new()
        ---->创建一个Pipeline及其bus,并完成pipeline函数的映射。
    2.mct_pipeline_start_session()
        ---->开启camera的所有模块并查询其能力
    3.pthread_create(..., mct_controller_thread_run, ...)
        ---->创建mct线程并开始执行
    4.pthread_create(..., mct_bus_handler_thread_run, ...)
        ---->创建bus处理线程

3.mct_list_traverse()函数
    此函数在整个mct线程中大量使用,主要用来遍历所有模块并执行一些处理工作。结合前面所讲,camera各模块都具有统一的接口,通过流来连接,模块中又包含模块,根据这种特性高通使用链表结构来保存这些模块并设计了此函数用来完成遍历操作。
    1.先来看看此链表的节点结构。链表的节点其实也是一个链表,整个链表就好像是一串串同级的节点搭建而成,整个数据结构组成一颗树结构。
        struct _mct_list {

            void             *data;  // 节点数据

            mct_list_t    *prev;  // 上一个节点地址

            mct_list_t    **next; // 下一个节点节点元素数组首地址

            uint32_t       next_num; // 下一个节点节点元素数,大部分情况下为1
        }mct_list_t;

    2.通过递归的深度优先算法来遍历整棵树。

 

4.MCT线程运行

    MCT整个引擎部分主要处理server及bus两类事情,对应前面提到的MCT及bus两个线程。MCT线程主要用来处理来自image server的消息,先pop MCT queue,查看是否有消息,如果有则执行mct_controller_proc_serv_msg_internal()函数来处理。

    mct_controller_proc_serv_msg_internal函数用来处理来自image server的消息,并返回类型MCT_PROCESS_RET_SERVER_MSG。这里处理的消息类型主要有SERV_MSG_DS与SERV_MSG_HAL两种,分别在pipline中给出了相应的处理函数,具体查看源码可知。

 

5.bus线程运行

    bus线程跟MCT线程流程一样。从代码上我们看到两个线程都是从同一个queue上面pop消息,他们是通过各自的线程条件变量来进行区分,完成线程的阻塞及运行工作。MCT的条件变量mctl_cond可以看到是在server_process.c文件中标记的,而bus的条件变量mctl_bus_handle_cond未在源码中找到标志的位置?


sensor模块

1.概述

    sensor模块是众多模块中的一个,主要是由模组的各个硬件模块组成,包括sensor、Flash、Af、EEprom、OIS、CSI等。这个模块主要描述了模组硬件的一些工作原理及部分驱动相关部分。

 

2.module_sensor_init()函数

    在前面讲到的server process中提到,服务进程开始后会初始化各个模块,其中就包括sensor模块,sensor初始化入口函数即为module_sensor_init(...)。这个函数将创建sensor模块并返回其指针,另外将创建它的端口,填充一些功能函数等。它的主要执行流程如下:

    1.创建sensor的MCT module。  —— mct_module_create(name)

        创建完之后填充set mode、query mode、start session、stop session及set session data五个接口函数。

    2.创建module_sensro_ctrl_t结构体,此结构体包含bundle信息,用来构建前面提到的模块树(方便添加、遍历等操作)。

    3.sensor模块是source模块,所以其numsinkports应该设置为0。

    4.eebin相关的操作

    5.sensor的探测操作,用来探测有效的sensor。

    6.填入所有已探测到sensor的信息。

    7.填入所以sensor的其它信息(Actuator,Flash,CSID,OIS等)。

    8.初始化sensor模块。

    9.创建基于CID info的端口

    10.初始化eeprom


马达驱动

1.概述

    actuator驱动信息文件是指vendor目录下的$ActuatorName_actuator.h文件(如gigaset_actuator.h)。此信息文件就是一个actuator_driver_ctrl_t结构体。包括actuator_params与actuator_tuned_params两部分,即driver与tunning两部分。文档以目前最主流的VCM为例进行参数的介绍。

 

2.driver部分信息——actuator_params

    act_type: actuator类型,目前就VCM、PIEZO及HVCM三种

    data_size:数据长度(VCM指传输lens位置的数据长度?)

    init_settings:actuator的初始化参数

    reg_tbl:寄存器列表,用来下actuator移动参数,这些参数又包括如下一些参数选项

    reg_write_type:写入数据的格式,VCM一般使用DAC,10位表示0~1023

      reg_addr:写入数据寄存器地址,如果不需要具体地址,则配置为0xFFFF

      data_shift:数据对应寄存器的移位

      hw_mask:其它bit位的控制设置,比如VCM的ring_ctrl

      hw_shift:控制设置对应寄存器的移位

reg_tbl部分举例AD5823如下:

      .reg_tbl =

      {

        .reg_tbl_size = 1,

        .reg_params =

        {

          {

            .reg_write_type = MSM_ACTUATOR_WRITE_DAC,

            .hw_mask = 0x00000400, 除data外的其它b it位的mask

            .reg_addr = 0x04, //寄存器写地址为reg04

            .hw_shift = 0, //hw_mask已经制定了明确的bit位,所以不再需要移位

            .data_shift = 0, //reg05[7:0]为数据,所以不需要移位

          },

        },

      },

    查看驱动代码大致如下:

value = (next_lens_position <<

                                     write_arr[i].data_shift) |

                                     ((hw_dword & write_arr[i].hw_mask) >>

                                     write_arr[i].hw_shift);

    最终获得I2C数据代码如下:

            i2c_byte1 = (value & 0xFF00) >> 8;   // 对应reg04寄存器值

                            i2c_byte2 = value & 0xFF;  //对应reg05寄存器值

    这里获得的value及需要写入寄存器的值,上面的hw_dword参数来自于tunning参数表中的hw_params (此驱动的hw_params值为0x00000400)。下面再来看看AD5823的寄存器介绍:

 

上图中reg04的bit[3]设置是否开启RING_CTRL模式,bit[7:4]为零,由前面讲到的hw_dword及hw_mask来设置。

再举例DW9714如下:

        .reg_params =

        {

          {

            .reg_write_type = MSM_ACTUATOR_WRITE_DAC,

            .hw_mask = 0x0000000F, //除data外的其它b it位的mask

            .reg_addr = 0xFFFF, //没有寄存器地址

            .hw_shift = 0, //hw_mask已经制定了明确的bit位,不再需要移位

            .data_shift = 4, //10bit的数据移位

          },

        },

再看DW9714规格书寄存器介绍如下:

 

    与代码对比可以看出,byte2的S[3:0]对应给出了mask位,需要根据tunning参数的hw_dword来确定具体的模式,此驱动给出的tunning参数为0x00000004,即S[3:0]-0100;而PD及FLAG被设置为0。

 

3. tunning部分——actuator_tuned_params

    tunning参数分为向近景方向移动与向远景方向移动两部分(MOVE_NEAR&MOVE_FAR),每个部分又可设定多个移动场景,各个场景参数可以单独设定,以便实现更细化的控制。

    高通在这里实现了一个lens位置数组,数组中的具体值对应着实际的lens位置,所以在驱动中可以看到step_pos与lens_pos两个参数,分别对应词数组表的下标index及当前index对应的值(此值为lens的实际位置)。代码体现如下:

        curr_lens_pos = a_ctrl->step_position_table[a_ctrl->curr_step_pos];

具体一些参数介绍如下:

    initial_code:初始lens的位置,对应VCM的0~1023的范围

    scenario_size:向近景与远景方向移动部分的场景数

    ringing_scenario: 各对应场景向近景与远景方向的移动步数最大值

    region_size:对lens的移动进行多个区段划分,可实现更细化的设置(针对非线性?)

    region_params:对应每个区段的在移动步数表中的index范围,即bound

        step_bound:对应具体的macro及infinity的bound边界值,所有区段合起来就是总                    长度(对应上面提到的表的长度)

        code_per_step:在当前分区中每个step对应移动的实际lens长度

    上面这些参数一起最终声场移动步数表数组,具体在msm_actuator_init_step_table函数中实现,具体可查看代码(msm_actuator.c文件),关键语句:

      cur_code = set_info->af_tuning_params.initial_code;  //初始值

      code_per_step =a_ctrl->region_params[region_index].code_per_step; //当前分区的步长

      cur_code += code_per_step;  //每步叠加一个步长

    damping:这部分参数用来做刹车消除磁滞,与前面的场景参数相呼应。

        ringing_params:各个具体场景对应的参数设置。下面三个参数为当前场景各区段的参数设置。

            damping_step:  // 对应场景lens每次移动的最大步长

            damping_delay: // 对应场景lens每次移动I2C命令间的延时

            hw_params:  // 对应场景actuator硬件寄存器的控制设置

  

    上面写了那么多,总结起来也就下面几点:

    1.有一个数组lens_pos[max_bound] -----> 对应前面多次提到的移动步数数组

    2.为了更好的消除磁滞,设定了多种移动的规则,根据每次移动的最大step来确定使用哪个规则---->规则即前面讲的场景,最大step就是前面的ringing_scenario数组中对应每个场景的值,这个值表示数组的下标。

    3.将整个移动范围分成多个区间,细分每个区间lens步进,针对线性化问题改善。

    4.针对不同场景的不同区间设定磁滞消除参数。

    另外要注意的一点是高通AF算法发出的参数为step数量,而不是具体的lens位置,具体lens位置需要查表获得。其实从这部分参数设定来看可以发现这里主要对驱动芯片的驱动方式进行设定,算法上前进步长的判断还是在其它地方实现,只要控制了每次的step长度,也就相当于进行了磁滞控制,所以参数damping_step设置为最大值是可以理解的。

下面举例来说明一下:

.actuator_tuned_params =

    {

      .scenario_size =

      {

        1, /* MOVE_NEAR */  // 场景数量,这里就定义了一个场景

        1, /* MOVE_FAR */

      },

      .ringing_scenario =

      {

        /* MOVE_NEAR */

        {

          400, //对应场景最大步子,移动步数小于此值表示适用此场景参数。这里只定义了一个场景,所以将此值设置为最大步数(即数组容量)。此值表示每次移动步数的步子大小判断,不是具体的数组下标。

        },

        /* MOVE_FAR */

        {

          400,

        },

      },

      .initial_code = 70,  // 初始lens的位置

      .region_size = 1,  //对lens的移动进行多个区段划分,用于不同区间磁滞的参数设定

      .region_params =

      {

        {

          .step_bound =  // 对应每个区段的step bound,这里只定义了一个区间,即整个VCM移动过程使用同样的磁滞处理。

          {

            400, /* Macro step boundary*/ // 当前区段的结束step

            0, /* Infinity step boundary*/ //当前区段的起始step

          },

          .code_per_step = 1,  // step表中lens的步进长度

        },

      },

      .damping =

      {

        /* damping[MOVE_NEAR] */

        {

          /* Scenario 0 */  // 当前场景的磁滞参数

          {

            .ringing_params =

            {

              /* Region 0 */  // 当前区间的磁滞参数

              {

                .damping_step = 0x3FF,  //这里设置为最大值1023,相当于不做磁滞处理,直接一次设定到需要的值。

                .damping_delay = 6000,

                .hw_params = 0x00000000,

              },

            },

          },

        },

        /* damping[MOVE_NEAR] */

        {

          /* Scenario 0 */

          {

            .ringing_params =

            {

              /* Region 0 */

              {

                .damping_step = 0x3FF,

                .damping_delay = 6000,

                .hw_params = 0x00000000,

              },

            },

          },

        },

      },

    }, /* actuator_tuned_params */

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值