对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动

对国产板子有阴 ? 些软硬件开源 AR M 开发板可以学 Linu x 驱动开

为了点亮一 MIP I 屏幕,我们除了要了 MIPI DS I 的工作原理之外,大前提要了解整 MIPI DS I 图显系统的组成,更需要清楚点亮一 MIP I 屏幕需要做哪些事情。

// / 插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以 ~ 点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比 ///

正文开始:

1. MIPI DS I 图显系统组成

MIP I 图显系统的硬件组成如下图表示:

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动 MIPI DS I 图显系统组成

图显处理器通 DP I 接口将像素数据传输 MIPI DSI Hos t MIPI D-PH Y 作为显示屏 DSI Hos t 之间由物理媒介,将编码后的像素数据发送 MIP I 显示屏。

MIPI DS I 显示屏而言,无 MIP I 信号转换的称之 pane l ,内部有数据信号转换桥片的称之 bridg e

MIP I 图显系统除了基本的像素数据信号外,为了使整个显示系统能够正常工作,还包含其他与显示屏相关的控制信号,包括显示屏内 I C 配置、显示屏背光配置、显示屏的复位和上电配置。

对于点亮一 MIP I 屏幕而言,重中之重是要正确的配置显示屏参数,配置方式主要有如 3 种:

· I2 C SP I 等总线配置

· 显示屏内部集成 MC U 完成配置

· MIPI DSI DC S 初始化序列

PW M 来实 MIP I 屏幕的背光控制,使 GPI O 完成显示屏的复位、上电的控制。

在设备树中定 MIPI DS I 图显系统的联结关系。

RK339 9 为例,其提供了两路 MIPI DSI 通道,分别是 dsi@ff960000 dsi1: 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 = ;

...

RK339 9 芯片使用 Synops y DPH Y 。控制器 DPH Y 之间的关系如下图所示:

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动

MIPI DSI 设备树结点中有一个信息 MIP I 显示密切相关,那就是时钟信息。可以看出 MIPI DSI 需要三路时钟,分别是 ref pclk phy_cfg

2050 dsi: dsi@ff960000 {

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

...

2054 clocks = , ,

2055 ;

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

pclk MIPI DSI host AP B 时钟,用于配置 MIPI DSI host 寄存器以及中断等。 ref phy_cfg MIPI DPH Y 所需时钟。这两路时钟 MIPI DSI hos t 提供。其中 ref 时钟用 MIPI DPH Y PL L 产生主机侧的串行发送时钟。 phy_cfg 是在配 MIPI DPH Y 时使用。

4 知识体系搭建

MIPI DSI 同图显控制 vo p 之间在逻辑层面上的联结关系如下:

# 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 = ;

};

「后文所涉及到的代码部分,均基 DR M 架构实现」

2. 关键 数据结构

请注意以下几个关键的数据结构,后文 MIPI DS I 初始化以 MIP I 显示系统初始化均以它们为核心进行展开,包括各数据结构的例化工作和创建各数据结构之间的联结关系。

struct mipi_dsi_host

struct mipi_dsi_host {

struct device *dev;

const struct mipi_dsi_host_ops *ops;

struct list_head list;

};

该数据结构 DRM MIPI DS I 提供,用以描 MIPI DSI Hos t ,包 Hos t 的驱动设备 Hos t 提供的功能函 ( 后文会介绍具体功 ) Hos t 链表的设备注册、管理。

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 DS I 提供,用以描 DS I 设备信息,主要包 DS I 设备 lan e ( 4lane ) 、像素格 ( Hos t 决定, RGB88 8 RGB56 5 )

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 DS I 提供,用以描 DSI Hos t 所能提供的功能函数,包 DSI Hos t 和显示屏之间创建联结关系需要使用 attac h 、通 DSI Hos t 配置显示屏 transfe r

3. MIPI DS I 软件架构

DR M 的图显系统中 MIPI DS I 子系统主要 DSI COR E PANEL COR E 组成,二者内建连接关系后注册 DRM COR E 系统。

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动 MIPI DS I 软件架构

DR M 架构下,提供了 drm_mipi_dsi.c drm_panel.c 以及 drm_bridge.c 三个核心文件。用户 MIPI DS I 驱动以 drm_mipi_dsi.c 为核心进行代码编写,例 RK339 9 MIP I 驱动 dw-mipi-dsi.c 。各显示屏厂商的驱动代码基于后两者进行编写,例如较为广泛使用的 panel-simple.c

4. MIP I 图显系统初始化

从第一章内容来看,点 MIPI DS I 屏幕并能正常的显示图像, DR M 架构下需完成如下初始化工作:

. MIPI DSI Hos t 初始化

. MIPI DSI D-PH Y 初始化

. MIPI DSI 屏幕初始化

. MIPI DS I 各模块间的联结关系

为了实现以上初始化工作,我们需要在设备 DT S 文件中约定各种初始化参数,包括各个模块的功能参数以及各个模块之间的联结关系,在驱动代码中配置 I P 及外设硬件,并基 DR M 架构注册出各组件,从软件层面勾勒 MIPI DS I 的数据流路径。

4.1 MIPI DSI 初始化

RK339 9 使用数据结构 struct dw_mipi_dsi MIPI DS I 设备,该数据结构也是整 MIPI DS I 图显系统的核心,囊括了系统中的各个组件。

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;

};

DR M 架构相关」 DRM KM S 架构 MIPI DS I 图显系统也同样遵循其设定的组件规则 MIPI Hos t DRM encode r D-PH Y PANE L 部分属 DRM connecto r 。在实际使用过程种你可能发现这样一个现象,就是电路板并没有连 MIP I 屏幕,但 DRM connecto r 的连接状态依然 connecte d ,这是因 MIPI DS I 无法真实的检测到物理连接关系,通过软件定 DRM connecto r encode r 之间的连接关系,当一切都初始化成功的时候 connecto r 就处 connecte d 状态了。

struct drm_encoder encoder;

struct drm_connector connector;

struct drm_bridge *bridge;

struct drm_panel *panel;

MIPI DS I 主体」 这里所说的主体主要包 MIPI DSI Hos t D-PH Y bridg e panne l 。不仅需要表 MIPI DS I 图显系统的关键组成模块,也需要定义彼此之间硬件与软件层面的连接关系。

struct mipi_dsi_host dsi_host;

struct mipi_dphy dphy;

struct drm_bridge *bridge;

struct drm_panel *panel;

MIPI DS I 参数」 我们关心 MIPI DS I 参数主要包括物理硬件定义、时钟频率、复位控制等。而物理硬件定义又包 lan e 数目、通道数 lan e 速率等。

unsigned long mode_flags;

unsigned int lane_mbps; /* per lane */

u32 channel;

u32 lanes;

u32 format;

「显示参数」 MIPI DS I 图显系统不 HDM I VG A 那样可以通 EDI D 获取到显示参数,也同样不支持热插拔操作。其显示参数如分辨率均是在显示屏初始化时注册 drm_display_mod e 中,当我们 MIPI DS I 注册 DR M 系统中的时候,直接解 drm_display_mod e 数据结构获取显示参数。

struct drm_display_mode mode;

const struct dw_mipi_dsi_plat_data *pdata;

介绍 MIPI DS I 关键数据结构之后,接下来我们看 MIPI DS I 的初始化流程是什么样的。

概况来讲 MIPI DSI Hos t 初始化包括两部分内容:初始 MIPI DSI Host/D-PH Y 功能、构 MIPI DS I D-PH Y 、显示屏之间的联结关系。关 Host/D-PH Y 的配置细节,因为每一 I P 的实现不尽相同,故本篇文章不做具体分析。

初始化流程如下图所示:

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动 MIPI DS I 初始化流程

「初始化流程解析」

同所有设备驱动一样,在驱动代码的开始处解析设备树,这里只需要解 MIPI DS I 结点即可 So C 厂商会实现这部分设备树的定义,我们只需要关心时钟相关的选项即可。

MIPI DSI hos t D-PH Y 之间的初始化存在天生的联系,可以基于通 ph y 代码架构进 D-PH Y 初始化,也可以 Hos t 代码进 D-PH Y 初始化。

在整个 Hos t 初始化流程中,非常关键的一步是注 Host op s ,其存在的意义是 pane l bridg e Hos t 控制的一种机制。例 pane l 通过 .attach Hos t ,通过 .transfer DSI Hos t 发送屏幕初始化序列 DC S 包。

DR M 架构 MIPI DS I 图显系统,必须注 encode r connecto r 两个组件,这属于常规操作, HDM I VG A LVD S 等图显系统一样。此处我们需要重点关 encode r .enable ,该函数会完 MIPI DSI Hos t D-PH Y 的功能配置。如下图所示:

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动 Hos t D-PH Y 功能初始化

以上所 Hos t D-PH Y 相关初始化均成功之后,构 Hos t panel(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 初始化

本文只分 pane l 的初始化流程,对 bridg e 的初始化不做过多阐述。从原理上来讲,二者区别不大。在嵌入式领域,使用最多的 pane l 类型的屏幕。基 DR M 架构,有专门 pane l 驱动,如下所示:

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 DS I 显示屏,我们都可以基 panel-simple. c 编写代码,在其中例化进我们的屏幕配置。当然 LC D LVD S pane l 驱动也在这个文件中,要按需编写。

当我们拿到一 MIPI DS I 显示屏之后,首先需要确定其硬件连接关系,查看是否需要通 GPI O 控制上下电、确定屏幕参数配置方式 PW M 背光调节等。

GPI O 上下电的控制方式并不多见,若存在这种需求,则需要在设备树中配 pinctr l 子系统。

屏幕参数的配置方式比较灵活,在前面已经介绍过。

PW M 背光调节功能 MIPI DS I 屏幕的必备项,但这部分并不难,我们需要做的是挂载相应 PW M 背光驱动即可。

pane l 的初始化,其流程如下图所示:

对国产板子有阴影这些软硬件开源的ARM开发板可以学习Linux驱动 pane l 初始化流程

「初始化流程解析」 初始化的第一步是解 MIPI DS I 屏幕的参数,包括上电参数、背光控制 lan e 数量、图显时序参数等等。这个是需要我们自己在设备树文件中补充定义的,根据自己手中的屏幕量身定制。

&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 DC S 配置屏幕,则还需要定义 init-sequence 初始化序列,通 DC S 配置屏幕时,需要注册 panel prepar e 函数, MIPI DSI Hos t 使能时回调到它,然通 MIPI DSI hos t .transfer DC S 数据的发送。例如 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( ) DC S 发送函数:

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 Hos t op s 函数:

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-lis t 链表以使 DR M 架构下可以找到对应 pane l 组件。