close

開啟MIPI圖顯旅途的第一步,就是將MIPI顯示屏連接到RK3399開發板,像下面這樣:

二者在硬件上實實在在的通過排線連接到了一起,那麼在軟件世界中是什麼樣子呢?通過本文的介紹,便可以揭開它神秘的面紗

首先,在DRM架構下,將圖顯控制器VOP的時序控制部分抽象為CRTC,將MIPI DSI的控制器部分抽象為ENCODER。如下圖所示:

其次,在DRM框架下,CRTC與ENCODER獨立註冊、統一綁定,二者也同樣存在着某種連接關係。

關於CRTC與ENCODER相關的知識,已經在前文進行了介紹,請查看如下鏈接:

一文講述| kernel圖顯系統的DRM CRTC模塊

一文講述| kernel圖顯系統的DRM ENCODER和CONNECTOR模塊

1. 定義聯結關係

在軟件代碼中,二者的聯結關係通常是靜態的。在設備樹中定義拓撲關係。MIPI DSI定義如下:

dsi:dsi@ff960000{compatible="rockchip,rk3399-mipi-dsi";...ports{port{#address-cells=<1>;#size-cells=<0>;...dsi_in_vopl:endpoint@1{reg=<1>;remote-endpoint=<&vopl_out_dsi>;};};};};

VOP定義如下:

vopl:vop@ff8f0000{compatible="rockchip,rk3399-vop-lit";...vopl_out:port{#address-cells=<1>;#size-cells=<0>;vopl_out_dsi:endpoint@0{reg=<0>;remote-endpoint=<&dsi_in_vopl>;};...};};

關於port、endpoint在前文進行了介紹,請查看如下鏈接:

RK3399圖顯系統在DRM架構下的設備樹拓撲結構解析

靜態定義好ENCODER和CRTC的拓撲關係後,在驅動代碼中通過解析設備樹實現二者的實際關聯。

2. CRTC註冊聯結關係

vop進行綁定時會調用vop_create_crtc()創建CRTC設備,在此過程中解析其device->of_node的port屬性,將對應的port例化給CRTC的port成員。

staticintvop_create_crtc(structvop*vop){...port=of_get_child_by_name(dev->of_node,"port");if(!port){DRM_ERROR("noportnodefoundin%s\n",dev->of_node->full_name);ret=-ENOENT;gotoerr_cleanup_crtc;}drm_flip_work_init(&vop->fb_unref_work,"fb_unref",vop_fb_unref_worker);init_completion(&vop->dsp_hold_completion);init_completion(&vop->line_flag_completion);crtc->port=port;...}

在這個過程中,CRTC對應的port信息會更新進DRM維護的crtc_list鍊表。master設備驅動中通過調用drm_mode_config_init()進行鍊表初始化。

voiddrm_mode_config_init(structdrm_device*dev){...INIT_LIST_HEAD(&dev->mode_config.fb_list);INIT_LIST_HEAD(&dev->mode_config.crtc_list);INIT_LIST_HEAD(&dev->mode_config.connector_list);...}EXPORT_SYMBOL(drm_mode_config_init);

crtc_list中會保存DRM設備中已經註冊的CRTC,他的生命周期貫穿始終。

crtc_list是ENCODER和CRTC之間聯結關係的橋樑。

3. ENCODER查找聯結關係

我們的環境中ENCODER實例對應着MIPI DSI的控制器部分。當進行MIPI DSI綁定時調用dw_mipi_dsi_register(),查找對應的CRTC。

staticintdw_mipi_dsi_bind(structdevice*dev,structdevice*master,void*data){structdrm_device*drm=data;structdw_mipi_dsi*dsi=dev_get_drvdata(dev);intret;dsi->panel=of_drm_find_panel(dsi->client);if(!dsi->panel){dsi->bridge=of_drm_find_bridge(dsi->client);if(!dsi->bridge)return-EPROBE_DEFER;}if(dsi->id){dsi->master=dw_mipi_dsi_find_by_id(dev->driver,0);if(!dsi->master)return-EPROBE_DEFER;}if(dsi->lanes>4){dsi->slave=dw_mipi_dsi_find_by_id(dev->driver,1);if(!dsi->slave)return-EPROBE_DEFER;dsi->lanes/=2;dsi->slave->lanes=dsi->lanes;dsi->slave->channel=dsi->channel;dsi->slave->format=dsi->format;dsi->slave->mode_flags=dsi->mode_flags;}ret=dw_mipi_dsi_register(drm,dsi);if(ret){dev_err(dev,"Failedtoregistermipi_dsi:%d\n",ret);returnret;}dev_set_drvdata(dev,dsi);dw_mipi_dsi_rpm_enable(dsi);returnret;}

我們先看dw_mipi_dsi_register()的函數定義,如下:

staticintdw_mipi_dsi_register(structdrm_device*drm,structdw_mipi_dsi*dsi){structdrm_encoder*encoder=&dsi->encoder;structdrm_connector*connector=&dsi->connector;structdevice*dev=dsi->dev;intret;encoder->possible_crtcs=drm_of_find_possible_crtcs(drm,dev->of_node);if(encoder->possible_crtcs==0)return-EPROBE_DEFER;drm_encoder_helper_add(&dsi->encoder,&dw_mipi_dsi_encoder_helper_funcs);ret=drm_encoder_init(drm,&dsi->encoder,&dw_mipi_dsi_encoder_funcs,DRM_MODE_ENCODER_DSI,NULL);if(ret){dev_err(dev,"Failedtoinitializeencoderwithdrm\n");returnret;}...}

dw_mipi_dsi_register()函數的兩個入參分別是struct drm_device以及struct dw_mipi_dsi。其中drm_device是由master決定,在我們的環境中它是display-subsystem,如設備樹中定義:

display_subsystem:display-subsystem{compatible="rockchip,display-subsystem";ports=<&vopl_out>,<&vopb_out>;clocks=<&cruPLL_VPLL>,<&cruPLL_CPLL>;clock-names="hdmi-tmds-pll","default-vop-pll";devfreq=<&dmc>;status="disabled";};

struct dw_mipi_dsi代表我們的MIPI DSI控制器,在此處用於查找其在設備樹中對應的CRTC(vop)。通過調用DRM架構中定義的drm_of_find_possible_crtcs()查找其對應的CRTC。

uint32_tdrm_of_find_possible_crtcs(structdrm_device*dev,structdevice_node*port){structdevice_node*remote_port,*ep;uint32_tpossible_crtcs=0;for_each_endpoint_of_node(port,ep){if(!of_device_is_available(ep)){of_node_put(ep);continue;}remote_port=of_graph_get_remote_port(ep);if(!remote_port){of_node_put(ep);return0;}possible_crtcs|=drm_crtc_port_mask(dev,remote_port);of_node_put(remote_port);}returnpossible_crtcs;}

在這個函數中首先解析設備樹,獲取對應的靜態聯結關係。drm_crtc_port_mask()函數遍歷crtc_list。

#definedrm_for_each_crtc(crtc,dev)\list_for_each_entry(crtc,&(dev)->mode_config.crtc_list,head)

若獲取到的encoder->possible_crtcs非0,則說明當前ENCODER和CRTC間已存在了正常的聯結關係。後面則可以繼續ENCODER的其餘初始化流程。


END

我的微信

--- 往期精彩內容 ---
內核圖顯子系統專輯內核驅動原理專輯內核驅動調試專輯內核及32/64位ARM處理器工作原理專輯
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()