对国产板子有阴 影 ? 这 些软硬件开源 的 AR M 开发板可以学 习 Linu x 驱动开 发
为了点亮一 块 MIP I 屏幕,我们除了要了 解 MIPI DS I 的工作原理之外,大前提要了解整 个 MIPI DS I 图显系统的组成,更需要清楚点亮一 块 MIP I 屏幕需要做哪些事情。
// / 插播一条:我自己在今年年初录制了一套还比较系统的入门单片机教程,想要的同学找我拿就行了免費的,私信我就可以 哦 ~ 点我头像黑色字体加我地球呺也能领取哦。最近比较闲,带做毕设,带学生参加省级或以上比 赛 ///
正文开始:
1. MIPI DS I 图显系统组成
MIP I 图显系统的硬件组成如下图表示:
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 之间的关系如下图所示:
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 系统。
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 的实现不尽相同,故本篇文章不做具体分析。
初始化流程如下图所示:
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 的功能配置。如下图所示:
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 的初始化,其流程如下图所示:
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 组件。