第一個(gè)攝像頭驅(qū)動(dòng)移植

操作步驟

1. 設(shè)備樹文件的修改

新平臺(tái)sdm429的設(shè)備樹文件路徑為kernel/msm-4.9/arch/arm64/boot/dts/qcom/,因?yàn)樾薷牡氖菙z像頭設(shè)備信息,修改文件為sdm429w-camera-sensor-spyro.dtsi

qcom,camera@1 {
        cell-index = <1>;   //sensor標(biāo)識(shí)
        compatible = "qcom,camera";   //設(shè)備與驅(qū)動(dòng)相匹配的屬性
        reg = <0x1>;  
        qcom,csiphy-sd-index = <1>;    //s_bundle->sensor_info->subdev_id[SUB_MODULE_CSIPHY] = 1
        qcom,csid-sd-index = <1>;     //s_bundle->sensor_info->subdev_id[SUB_MODULE_CSID] = 1
        qcom,mount-angle = <270>;
        cam_vdig-supply = <&pm660_l3>;    //DVDD供電(isp供電)
        cam_vio-supply = <&pm660_l14>;    //IOVDD供電(i2c供電)
        cam_vaf-supply = <&pm660_l19>;     //馬達(dá)供電
        qcom,cam-vreg-name = "cam_vdig","cam_vana","cam_vio",        //供電輸出配置
                            "cam_vaf";            
        qcom,cam-vreg-min-voltage = <1200000 2800000 1800000 2850000>;
        qcom,cam-vreg-max-voltage = <1200000 2800000 1800000 3200000>;
        qcom,cam-vreg-op-mode = <200000 0 80000 100000>;
        pinctrl-names = "cam_default", "cam_suspend";
        pinctrl-0 = <&cam_sensor_mclk1_default
                &cam_sensor_front_default>;
        pinctrl-1 = <&cam_sensor_mclk1_sleep
                &cam_sensor_front_sleep>;
        gpios = <&tlmm 27 0>,            //GPIO口配置
            <&tlmm 33 0>,
            <&tlmm 66 0>,
            <&tlmm 38 0>;
        qcom,gpio-vana= <1>;
        qcom,gpio-vdig= <2>;
        qcom,gpio-reset = <3>;
        qcom,gpio-req-tbl-num = <0 1 2 3>;
        qcom,gpio-req-tbl-flags = <1 0 0 0>;
        qcom,gpio-req-tbl-label = "CAMIF_MCLK1",
            "CAM_AVDD1",
            "CAM_DVDD1",
            "CAM_RESET1";
        qcom,sensor-position = <1>;   //后攝為0 前攝為1
        qcom,sensor-mode = <0>;     
        qcom,cci-master = <0>;  //一般來說只要前后攝i2c地址不沖突奠衔,配置為0即可
        clocks = <&clock_gcc clk_mclk1_clk_src>,                 
                <&clock_gcc clk_gcc_camss_mclk1_clk>;
        clock-names = "cam_src_clk", "cam_clk";
        qcom,clock-rates = <24000000 0>;
    };

2. 驅(qū)動(dòng)文件和效果文件添加

在vendor/qcom/proprietary/mm-camera/mm-camera2/mediacontroller/modules/sensors/sensor/libs目錄下新建s5k4h7目錄谆刨,在該目錄下添加s5k4h7驅(qū)動(dòng)文件,并編寫Android.mk文件涣觉。在vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/chromatix/0310目錄下添加效果文件痴荐。

3. 修改device-vendor.mk

vi vendor/qcom/proprietary/common/config/device-vendor.mk,添加新增的模塊血柳。


device-vender.mk

4. 修改msm8937_camera.xml

在vendor/qcom/proprietary/mm-camera/mm-camera2/media-controller/modules/sensors/configs/msm8937_camera.xml文件中添加如下CameraModuleConfig節(jié)點(diǎn):

<CameraModuleConfig>
    <CameraId>1</CameraId>
    <SensorName>s5k4h7</SensorName>
    <ChromatixName>s5k4h7_chromatix</ChromatixName>
    <ModesSupported>1</ModesSupported>
    <Position>BACK</Position>
    <MountAngle>90</MountAngle>
    <CSIInfo>
      <CSIDCore>0</CSIDCore>
      <LaneMask>0x1F</LaneMask>
      <LaneAssign>0x4320</LaneAssign>
      <ComboMode>0</ComboMode>
    </CSIInfo>
    <LensInfo>
      <FocalLength>2.94</FocalLength>
      <FNumber>2.4</FNumber>
      <TotalFocusDistance>1.5</TotalFocusDistance>
      <HorizontalViewAngle>63.2</HorizontalViewAngle>
      <VerticalViewAngle>49.7</VerticalViewAngle>
      <MinFocusDistance>0.1</MinFocusDistance>
    </LensInfo>
</CameraModuleConfig>

原理分析

驅(qū)動(dòng)移植雖然步驟不多官册,但是麻煩卻一大堆,搞清楚其原理是一件很重要的事情难捌。這里首先梳理下攝像頭的初始化流程:在內(nèi)核初始化過程中會(huì)將qcom,camera@1節(jié)點(diǎn)轉(zhuǎn)換為platform_device,當(dāng)設(shè)備和驅(qū)動(dòng)匹配會(huì)調(diào)用驅(qū)動(dòng)的probe函數(shù)膝宁,該函數(shù)讀取設(shè)備節(jié)點(diǎn)信息并保存。另外根吁,Qualcomm的camera守護(hù)進(jìn)程端獲取需要初始化的sensor员淫,然后加載其驅(qū)動(dòng)程序,并將驅(qū)動(dòng)信息傳遞給內(nèi)核击敌,內(nèi)核對其上電測試成功后創(chuàng)建/dev/video1設(shè)備文件節(jié)點(diǎn)介返。可以看出其最終目的還是創(chuàng)建一個(gè)設(shè)備文件節(jié)點(diǎn)給上層訪問沃斤。
這里以sensor_init.c作為切入點(diǎn)圣蝎,sensor_init_probe->sensor_init_xml_probe

static boolean sensor_init_xml_probe(module_sensor_ctrl_t *module_ctrl,
  int32_t sd_fd){
    ...
    num_cam_config = sensor_xml_util_get_num_nodes(rootPtr, "CameraModuleConfig");  //獲取需要配置的sensor數(shù)量
    for (i = 0; i < num_cam_config; i++) {
        nodePtr = sensor_xml_util_get_node(rootPtr, "CameraModuleConfig", i); //獲取第i個(gè)CameraModuleConfig節(jié)點(diǎn)
        xmlConfig.nodePtr = nodePtr;
        ret = sensor_xml_util_get_camera_probe_config(&xmlConfig, "CameraModuleConfig");  //獲取probe配置信息
        rc = sensor_probe(module_ctrl,
                      sd_fd,
                      camera_cfg.sensor_name,
                      NULL,
                      &xmlConfig,
                      FALSE,
                      FALSE);
        ...
  }

可以看出該函數(shù)是遍歷xml文件中的CameraModuleConfig節(jié)點(diǎn),解析節(jié)點(diǎn)信息衡瓶,至于遍歷的是哪個(gè)xml文件徘公,可以從日志文件中分析,然后推出需要配置的xml文件哮针。sensor_probe分析:

static boolean sensor_probe(...){
    ...
    rc = sensor_load_library(sensor_name, sensor_lib_params, path);
    translate_sensor_slave_info(slave_info,
      &sensor_lib_params->sensor_lib_ptr->sensor_slave_info,
      xmlConfig->configPtr, power_up_setting, power_down_setting);
    memset(&cfg, 0, sizeof(cfg));
    cfg.cfgtype = CFG_SINIT_PROBE;
    cfg.cfg.setting = slave_info;
    if (ioctl(fd, VIDIOC_MSM_SENSOR_INIT_CFG, &cfg) < 0) {
        SINFO("[%s]CFG_SINIT_PROBE failed",sensor_name);
        ret = FALSE;
        goto ERROR;
    }  //發(fā)送probe指令給sensor_init
    ...
}

sensor_load_library主要是加載sensor的驅(qū)動(dòng)信息关面,通過sensor name加載對應(yīng)的.so文件,然后執(zhí)行sensor_open_lib函數(shù)十厢,獲取sensor的各項(xiàng)配置參數(shù)等太,查看s5k4h7的驅(qū)動(dòng)程序,該函數(shù)返回一個(gè)sensor_lib_t結(jié)構(gòu)體地址蛮放。然后將各參數(shù)傳入slave_info澈驼,傳給內(nèi)核。調(diào)用sensor_init設(shè)備的ioctl接口筛武,最終調(diào)用到msm_sensor_driver_probe:

int32_t msm_sensor_driver_probe(...){
    ...
    if (copy_from_user(slave_info,(void __user *)setting, sizeof(*slave_info))) {
        pr_err("failed: copy_from_user");
        rc = -EFAULT;
        goto free_slave_info;
    }   //獲取mm-camera傳來的slave_info
    s_ctrl = g_sctrl[slave_info->camera_id]; //獲取msm_sensor_ctrl_t
    rc = msm_sensor_get_power_settings(setting, slave_info,
      &s_ctrl->sensordata->power_info);  //獲取上下電流程
  
    camera_info = kzalloc(sizeof(struct msm_camera_slave_info), GFP_KERNEL);
    s_ctrl->sensordata->slave_info = camera_info;
    /* Fill sensor slave info */
    camera_info->sensor_id_reg_addr = slave_info->sensor_id_info.sensor_id_reg_addr;
    camera_info->sensor_id = slave_info->sensor_id_info.sensor_id; //保存sensor驅(qū)動(dòng)程序配置信息
    ...
    rc = s_ctrl->func_tbl->sensor_power_up(s_ctrl); //sensor上電和probe
    ...
    if (s_ctrl->sensor_device_type == MSM_CAMERA_PLATFORM_DEVICE)
        rc = msm_sensor_driver_create_v4l_subdev(s_ctrl);
    else
        rc = msm_sensor_driver_create_i2c_v4l_subdev(s_ctrl);
    ...
}

s_ctrl->func_tbl->sensor_power_up=>msm_sensor_power_up缝其,sensor上電分為兩個(gè)步驟挎塌,第一步給sensor上電并初始化cci,第二步匹配id内边。因?yàn)槊總€(gè)sensor都是作為一個(gè)slave掛在cci總線上榴都,而sdm429則是作為一個(gè)master,他們通訊需要遵循cci協(xié)議漠其,所以只有sensor上電成功嘴高,sdm429才能與其通信,讀取其id和屎,而第二步id匹配則是比較slave_info中的id和讀取的id是否相同拴驮。
g_sctrl是一個(gè)全局變量,在platform_driver的probe函數(shù)中填充柴信,這是一個(gè)很重要的結(jié)構(gòu)體指針數(shù)組套啤,包含了所有sensor的所有信息,也就是說設(shè)備樹中有幾個(gè)qcom,camera@1節(jié)點(diǎn)随常,g_sctrl就有幾組數(shù)據(jù)潜沦,然后我們通過camera id索引得到對應(yīng)的msm_sensor_ctrl_t結(jié)構(gòu)體,該結(jié)構(gòu)體的成員變量sensordata包含了camera的板級信息(供電绪氛、io口)唆鸡,對應(yīng)著硬件資源,我們在probe的時(shí)候需要申請這些資源枣察,不然cpu也不知道如何給sensor供電了争占。那現(xiàn)在最關(guān)鍵的就是查看g_sctrl在哪里填充的了,閱讀代碼可以看出序目,每匹配到一個(gè)camera設(shè)備臂痕,msm_sensor_driver_platform_probe執(zhí)行一次,并申請一個(gè)msm_sensor_ctrl_t結(jié)構(gòu)體:

static int32_t msm_sensor_driver_platform_probe(struct platform_device *pdev)
{
    s_ctrl = kzalloc(sizeof(*s_ctrl), GFP_KERNEL);
    ...
    rc = msm_sensor_driver_parse(s_ctrl);
}
static int32_t msm_sensor_driver_parse(struct msm_sensor_ctrl_t *s_ctrl)
{
    rc = msm_sensor_driver_get_dt_data(s_ctrl); //獲取設(shè)備樹節(jié)點(diǎn)屬性
    ...
    g_sctrl[s_ctrl->id] = s_ctrl;
}
static int32_t msm_sensor_driver_get_dt_data(struct msm_sensor_ctrl_t *s_ctrl)
{
    ...
    rc = of_property_read_u32(of_node, "cell-index", &cell_id);
    s_ctrl->id = cell_id;
}

可以看出g_sctrl的填充索引值為設(shè)備樹中的cell-index宛琅,獲取該數(shù)組成員的索引值為sensor驅(qū)動(dòng)的slave_info->camera_id刻蟹,由xml文件讀取得到。
作為一個(gè)小白嘿辟,我開始只寫過裸板驅(qū)動(dòng)舆瘪,并不清楚驅(qū)動(dòng)、設(shè)備跟總線的關(guān)系红伦,所以一開始直接上手設(shè)備樹是很懵的英古,一開始并不明白寫了xxx_driver為什么還要寫xxx_device,后來在學(xué)習(xí)platform總線的時(shí)候又一直在找platform_device在哪里定義昙读,所以在此強(qiáng)烈建議小白學(xué)習(xí)一下設(shè)備驅(qū)動(dòng)模型linux設(shè)備樹

問題總結(jié)

1. read id failed

在日志文件中搜索關(guān)鍵字match_id可以得到如下信息


log

讀取id失敗一般情況下是由于上電失敗導(dǎo)致的召调,上電不成功,sensor是無法進(jìn)行cci通信的,上電失敗的原因這里總結(jié)可能有兩點(diǎn)唠叛,第一是自己id的匹配錯(cuò)誤只嚣,同一平臺(tái)的硬件資源是有限的,每個(gè)攝像頭插槽分配的資源也是固定的艺沼,即每個(gè)插槽的供電和gpio口是一定的册舞,所以可以將設(shè)備樹中的設(shè)備想象為對應(yīng)的camera插槽,當(dāng)你的sensor插在哪個(gè)插槽障般,xml文件中配置的camera id就要配置成相應(yīng)設(shè)備中的cell-index调鲸,不然上電肯定會(huì)失敗。第二個(gè)原因可能是上電時(shí)序配置錯(cuò)誤挽荡,一般情況下硬件廠商會(huì)提供驅(qū)動(dòng)代碼藐石,里面會(huì)有上電時(shí)序。

2. 刷機(jī)失敗

在使用QFile刷機(jī)時(shí)定拟,會(huì)報(bào)如下錯(cuò)誤:


qfile error

原因是在編譯過程中修改了源碼于微,導(dǎo)致雖然編譯成功,但是vbmeta.img是0字節(jié)办素,進(jìn)而導(dǎo)致燒寫失敗角雷,解決方案:重新編譯源碼祸穷。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末性穿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子雷滚,更是在濱河造成了極大的恐慌需曾,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祈远,死亡現(xiàn)場離奇詭異呆万,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)车份,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進(jìn)店門谋减,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人扫沼,你說我怎么就攤上這事出爹。” “怎么了缎除?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵严就,是天一觀的道長。 經(jīng)常有香客問我器罐,道長梢为,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮铸董,結(jié)果婚禮上祟印,老公的妹妹穿的比我還像新娘。我一直安慰自己粟害,他們只是感情好旁理,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布不脯。 她就那樣靜靜地躺著需忿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪璧帝。 梳的紋絲不亂的頭發(fā)上夺艰,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天芋哭,我揣著相機(jī)與錄音,去河邊找鬼郁副。 笑死减牺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的存谎。 我是一名探鬼主播拔疚,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼既荚!你這毒婦竟也來了稚失?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤恰聘,失蹤者是張志新(化名)和其女友劉穎句各,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晴叨,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡凿宾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了兼蕊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初厚。...
    茶點(diǎn)故事閱讀 40,090評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖孙技,靈堂內(nèi)的尸體忽然破棺而出产禾,到底是詐尸還是另有隱情,我是刑警寧澤绪杏,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布下愈,位于F島的核電站,受9級特大地震影響蕾久,放射性物質(zhì)發(fā)生泄漏势似。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望履因。 院中可真熱鬧障簿,春花似錦、人聲如沸栅迄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毅舆。三九已至西篓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間憋活,已是汗流浹背岂津。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留悦即,地道東北人吮成。 一個(gè)月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓,卻偏偏與公主長得像辜梳,于是被迫代替她去往敵國和親粱甫。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容