對國產板子有陰影這些軟硬體開源的ARM開發板可以學習Linux驅動

2022-04-25     大方老師單片機

原標題:對國產板子有陰影這些軟硬體開源的ARM開發板可以學習Linux驅動

對國產板子有陰?些軟硬體開源ARM開發板可以學Linux驅動開

為了點亮一MIPI螢幕,我們除了要了MIPI DSI的工作原理之外,大前提要了解整MIPI DSI圖顯系統的組成,更需要清楚點亮一MIPI螢幕需要做哪些事情。

///插播一條:我自己在今年年初錄製了一套還比較系統的入門單片機教程,想要的同學找我拿就行了免費的,私信我就可以~點我頭像黑色字體加我地球呺也能領取哦。最近比較閒,帶做畢設,帶學生參加省級或以上比///

正文開始:

1. MIPI DSI圖顯系統組成

MIPI圖顯系統的硬體組成如下圖表示:

MIPI DSI圖顯系統組成

圖顯處理器通DPI接口將像素數據傳輸MIPI DSI HostMIPI D-PHY作為顯示屏DSI Host之間由物理媒介,將編碼後的像素數據發送MIPI顯示屏。

MIPI DSI顯示屏而言,無MIPI信號轉換的稱之panel,內部有數據信號轉換橋片的稱之bridge

MIPI圖顯系統除了基本的像素數據信號外,為了使整個顯示系統能夠正常工作,還包含其他與顯示屏相關的控制信號,包括顯示屏內IC配置、顯示屏背光配置、顯示屏的復位和上電配置。

對於點亮一MIPI螢幕而言,重中之重是要正確的配置顯示屏參數,配置方式主要有如3種:

·I2CSPI等總線配置

·顯示屏內部集成MCU完成配置

·MIPI DSI DCS初始化序列

PWM來實MIPI螢幕的背光控制,使GPIO完成顯示屏的復位、上電的控制。

在設備樹中定MIPI DSI圖顯系統的聯結關係。

RK3399為例,其提供了兩路MIPI DSI通道,分別是dsi@ff960000dsi1: dsi@ff968000,代表MIPI DSI host

2050 dsi: dsi@ff960000 {

2051 compatible = "rockchip,rk3399-mipi-dsi";

2052 reg = ;

...

2083 dsi1: dsi@ff968000 {

2084 compatible = "rockchip,rk3399-mipi-dsi";

2085 reg = ;

...

RK3399晶片使用SynopsyDPHY。控制器DPHY之間的關係如下圖所示:

MIPI DSI設備樹結點中有一個信息MIPI顯示密切相關,那就是時鐘信息。可以看出MIPI DSI需要三路時鐘,分別是refpclkphy_cfg

2050 dsi: dsi@ff960000 {

2051 compatible = "rockchip,rk3399-mipi-dsi";

...

2054 clocks = , ,

2055 ;

2056 clock-names = "ref", "pclk", "phy_cfg";

pclkMIPI DSI hostAPB時鐘,用於配置MIPI DSI host寄存器以及中斷等。refphy_cfgMIPI DPHY所需時鐘。這兩路時鐘MIPI DSI host提供。其中ref時鐘用MIPI DPHYPLL產生主機側的串行發送時鐘。phy_cfg是在配MIPI DPHY時使用。

4知識體系搭建

MIPI DSI同圖顯控制vop之間在邏輯層面上的聯結關係如下:

# MIPI DSI Host

dsi_in_vopb: endpoint@0 {

reg = ;

remote-endpoint = ;

};

dsi_in_vopl: endpoint@1 {

reg = ;

remote-endpoint = ;

};

# VOP

vopb_out_dsi: endpoint@1 {

reg = ;

remote-endpoint = ;

};

vopb_out_dsi1: endpoint@4 {

reg = ;

remote-endpoint = ;

};

「後文所涉及到的代碼部分,均基DRM架構實現」

2.關鍵數據結構

請注意以下幾個關鍵的數據結構,後文MIPI DSI初始化以MIPI顯示系統初始化均以它們為核心進行展開,包括各數據結構的例化工作和創建各數據結構之間的聯結關係。

struct mipi_dsi_host

struct mipi_dsi_host {

struct device *dev;

const struct mipi_dsi_host_ops *ops;

struct list_head list;

};

該數據結構DRM MIPI DSI提供,用以描MIPI DSI Host,包Host的驅動設備Host提供的功能函(後文會介紹具體功)Host鍊表的設備註冊、管理。

struct mipi_dsi_device

struct mipi_dsi_device {

struct mipi_dsi_host *host;

struct device dev;

char name[DSI_DEV_NAME_SIZE];

unsigned int channel;

unsigned int lanes;

enum mipi_dsi_pixel_format format;

unsigned long mode_flags;

};

該數據結構DRM MIPI DSI提供,用以描DSI設備信息,主要包DSI設備lane(4lane)、像素格(Host決定,RGB888RGB565)

struct mipi_dsi_host_ops

struct mipi_dsi_host_ops {

int (*attach)(struct mipi_dsi_host *host,

struct mipi_dsi_device *dsi);

int (*detach)(struct mipi_dsi_host *host,

struct mipi_dsi_device *dsi);

ssize_t (*transfer)(struct mipi_dsi_host *host,

const struct mipi_dsi_msg *msg);

};

該數據結構DRM MIPI DSI提供,用以描DSI Host所能提供的功能函數,包DSI Host和顯示屏之間創建聯結關係需要使用attach、通DSI Host配置顯示屏transfer

3. MIPI DSI軟體架構

DRM的圖顯系統中MIPI DSI子系統主要DSI COREPANEL CORE組成,二者內建連接關係後註冊DRM CORE系統。

MIPI DSI軟體架構

DRM架構下,提供了drm_mipi_dsi.cdrm_panel.c以及drm_bridge.c三個核心文件。用戶MIPI DSI驅動以drm_mipi_dsi.c為核心進行代碼編寫,例RK3399MIPI驅動dw-mipi-dsi.c。各顯示屏廠商的驅動代碼基於後兩者進行編寫,例如較為廣泛使用的panel-simple.c

4. MIPI圖顯系統初始化

從第一章內容來看,點MIPI DSI螢幕並能正常的顯示圖像,DRM架構下需完成如下初始化工作:

.MIPI DSI Host初始化

.MIPI DSI D-PHY初始化

.MIPI DSI螢幕初始化

.MIPI DSI各模塊間的聯結關係

為了實現以上初始化工作,我們需要在設備DTS文件中約定各種初始化參數,包括各個模塊的功能參數以及各個模塊之間的聯結關係,在驅動代碼中配置IP及外設硬體,並基DRM架構註冊出各組件,從軟體層面勾勒MIPI DSI的數據流路徑。

4.1 MIPI DSI初始化

RK3399使用數據結構struct dw_mipi_dsiMIPI DSI設備,該數據結構也是整MIPI DSI圖顯系統的核心,囊括了系統中的各個組件。

struct dw_mipi_dsi {

struct drm_encoder encoder;

struct drm_connector connector;

struct drm_bridge *bridge;

struct device_node *client;

struct mipi_dsi_host dsi_host;

struct mipi_dphy dphy;

struct drm_panel *panel;

struct device *dev;

struct regmap *grf;

struct reset_control *rst;

struct regmap *regmap;

struct clk *pclk;

struct clk *h2p_clk;

int irq;

struct dw_mipi_dsi *master;

struct dw_mipi_dsi *slave;

struct mutex mutex;

bool prepared;

unsigned int id;

unsigned long mode_flags;

unsigned int lane_mbps; /* per lane */

u32 channel;

u32 lanes;

u32 format;

struct drm_display_mode mode;

const struct dw_mipi_dsi_plat_data *pdata;

};

DRM架構相關」

DRM KMS架構MIPI DSI圖顯系統也同樣遵循其設定的組件規則MIPI HostDRM encoderD-PHYPANEL部分屬DRM connector。在實際使用過程種你可能發現這樣一個現象,就是電路板並沒有連MIPI螢幕,但DRM connector的連接狀態依然connected,這是因MIPI DSI無法真實的檢測到物理連接關係,通過軟體定DRM connectorencoder之間的連接關係,當一切都初始化成功的時候connector就處connected狀態了。

struct drm_encoder encoder;

struct drm_connector connector;

struct drm_bridge *bridge;

struct drm_panel *panel;

MIPI DSI主體」

這裡所說的主體主要包MIPI DSI HostD-PHYbridgepannel。不僅需要表MIPI DSI圖顯系統的關鍵組成模塊,也需要定義彼此之間硬體與軟體層面的連接關係。

struct mipi_dsi_host dsi_host;

struct mipi_dphy dphy;

struct drm_bridge *bridge;

struct drm_panel *panel;

MIPI DSI參數」

我們關心MIPI DSI參數主要包括物理硬體定義、時鐘頻率、復位控制等。而物理硬體定義又包lane數目、通道數lane速率等。

unsigned long mode_flags;

unsigned int lane_mbps; /* per lane */

u32 channel;

u32 lanes;

u32 format;

「顯示參數」

MIPI DSI圖顯系統不HDMIVGA那樣可以通EDID獲取到顯示參數,也同樣不支持熱插拔操作。其顯示參數如解析度均是在顯示屏初始化時註冊drm_display_mode中,當我們MIPI DSI註冊DRM系統中的時候,直接解drm_display_mode數據結構獲取顯示參數。

struct drm_display_mode mode;

const struct dw_mipi_dsi_plat_data *pdata;

介紹MIPI DSI關鍵數據結構之後,接下來我們看MIPI DSI的初始化流程是什麼樣的。

概況來講MIPI DSI Host初始化包括兩部分內容:初始MIPI DSI Host/D-PHY功能、構MIPI DSID-PHY、顯示屏之間的聯結關係。關Host/D-PHY的配置細節,因為每一IP的實現不盡相同,故本篇文章不做具體分析。

初始化流程如下圖所示:

MIPI DSI初始化流程

「初始化流程解析」

同所有設備驅動一樣,在驅動代碼的開始處解析設備樹,這裡只需要解MIPI DSI結點即可SoC廠商會實現這部分設備樹的定義,我們只需要關心時鐘相關的選項即可。

MIPI DSI hostD-PHY之間的初始化存在天生的聯繫,可以基於通phy代碼架構進D-PHY初始化,也可以Host代碼進D-PHY初始化。

在整個Host初始化流程中,非常關鍵的一步是注Host ops,其存在的意義是panelbridgeHost控制的一種機制。例panel通過.attachHost,通過.transferDSI Host發送螢幕初始化序列DCS包。

DRM架構MIPI DSI圖顯系統,必須注encoderconnector兩個組件,這屬於常規操作,HDMIVGALVDS等圖顯系統一樣。此處我們需要重點關encoder.enable,該函數會完MIPI DSI HostD-PHY的功能配置。如下圖所示:

HostD-PHY功能初始化

以上所HostD-PHY相關初始化均成功之後,構Hostpanel(bridge)之間的聯結關係:

int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)

{

if (panel->connector)

return -EBUSY;

panel->connector = connector;

panel->drm = connector->dev;

return 0;

}

4.2 PANEL初始化

本文只分panel的初始化流程,對bridge的初始化不做過多闡述。從原理上來講,二者區別不大。在嵌入式領域,使用最多的panel類型的螢幕。基DRM架構,有專門panel驅動,如下所示:

rk@ubuntu:~/OK3399-linux-release/kernel$ ls drivers/gpu/drm/panel/

Makefile panel-samsung-ld9040.c panel-sharp-lq101r1sx01.c panel-simple.o

Kconfig panel-lg-lg4573.c panel-samsung-s6e8aa0.c panel-simple.c

對於大多數MIPI DSI顯示屏,我們都可以基panel-simple.c編寫代碼,在其中例化進我們的螢幕配置。當然LCDLVDSpanel驅動也在這個文件中,要按需編寫。

當我們拿到一MIPI DSI顯示屏之後,首先需要確定其硬體連接關係,查看是否需要通GPIO控制上下電、確定螢幕參數配置方式PWM背光調節等。

GPIO上下電的控制方式並不多見,若存在這種需求,則需要在設備樹中配pinctrl子系統。

螢幕參數的配置方式比較靈活,在前面已經介紹過。

PWM背光調節功能MIPI DSI螢幕的必備項,但這部分並不難,我們需要做的是掛載相應PWM背光碟機動即可。

panel的初始化,其流程如下圖所示:

panel初始化流程

「初始化流程解析」初始化的第一步是解MIPI DSI螢幕的參數,包括上電參數、背光控制lane數量、圖顯時序參數等等。這個是需要我們自己在設備樹文件中補充定義的,根據自己手中的螢幕量身定製。

&dsi {

panel@0{

status = "okay";

compatible ="simple-panel-dsi";

reg = ;

backlight = ;

re-delay-ms = ;

...

dsi,flags =

MIPI_DSI_MODE_LPM | MIPI_DSI_MODE_EOT_PACKET)>;

dsi,format = ;

dsi,lanes = ;

display-timings {

native-mode = ;

timing1: timing1 {

clock-frequency = ;

...

pixelclk-active = ;

};

};

};

};

若需要通MIPI DSI DCS配置螢幕,則還需要定義init-sequence初始化序列,通DCS配置螢幕時,需要註冊panel prepare函數,MIPI DSI Host使能時回調到它,然通MIPI DSI host.transferDCS數據的發送。例如panel_simple_prepare()函數:

static int panel_simple_prepare(struct drm_panel *panel)

{

struct panel_simple *p = to_panel_simple(panel);

int err;

...

if (p->on_cmds) {

if (p->dsi)

err = panel_simple_dsi_send_cmds(p, p->on_cmds);

else if (p->cmd_type == CMD_TYPE_SPI)

err = panel_simple_spi_send_cmds(p, p->on_cmds);

if (err)

dev_err(p->dev, "failed to send on cmds\n");

}

...

}

panel_simple_dsi_send_cmds()調DCS發送函數:

static int panel_simple_dsi_send_cmds(struct panel_simple *panel,

struct panel_cmds *cmds)

{

...

switch (cmd->dchdr.dtype) {

case MIPI_DSI_GENERIC_SHORT_WRITE_0_PARAM:

case MIPI_DSI_GENERIC_SHORT_WRITE_1_PARAM:

case MIPI_DSI_GENERIC_SHORT_WRITE_2_PARAM:

case MIPI_DSI_GENERIC_LONG_WRITE:

err = mipi_dsi_generic_write(dsi, cmd->payload,

cmd->dchdr.dlen);

break;

case MIPI_DSI_DCS_SHORT_WRITE:

case MIPI_DSI_DCS_SHORT_WRITE_PARAM:

case MIPI_DSI_DCS_LONG_WRITE:

err = mipi_dsi_dcs_write_buffer(dsi, cmd->payload,

cmd->dchdr.dlen);

break;

default:

return -EINVAL;

}

...

return 0;

}

最後調用MIPI DSI Hostops函數:

static ssize_t mipi_dsi_device_transfer(struct mipi_dsi_device *dsi,

struct mipi_dsi_msg *msg)

{

const struct mipi_dsi_host_ops *ops = dsi->host->ops;

if (!ops || !ops->transfer)

return -ENOSYS;

if (dsi->mode_flags & MIPI_DSI_MODE_LPM)

msg->flags |= MIPI_DSI_MSG_USE_LPM;

return ops->transfer(dsi->host, msg);

}

當以上具體的配置工作正常結束之後,更panel-list鍊表以使DRM架構下可以找到對應panel組件。

文章來源: https://twgreatdaily.com/zh-hk/b99408e54b5f281356c578fa885f8b2d.html