全文包括如下内容
- 组件系统介绍
- component组件
辅助单元的注册 - master组件
图显控制器的注册与所有辅助单元绑定
0. 引言
kernel的图显系统已经普遍采用 DRM 架构,SoC厂商结合各自图显处理器的特点来编写代码。图显系统包括图显处理器、时序控制设备、编解码设备、PHY等,每个设备的驱动代码独立编写,最后再组织成一个整体。
1. 组件系统介绍
上图里的玩具车由核心单元(控制器) + 辅助单元(车轮、底盘等)组成。首先安装一堆的辅助单元、最后安装控制单元。在安装控制单元的时候需要知道它和辅助单元之间的拼装关系。
kernel里的组件系统类比为上图的玩具车系统,由一个且仅支持一个的核心单元master以及一堆的辅助单元component组成。将这种思想应用到基于DRM的图显系统中,master代表图显控制器;辅助单元component,是一堆的图显控制器外围设备,如HDMI、MIPI、LVDS等等。
组件式的代码架构由Russell King设计,2014年1月份,首次提交到了内核3.13.0 rc5版本中。
author Russell King <rmk+kernel@arm.linux.org.uk> 2014-01-10 23:23:37 +0000
committer Greg Kroah-Hartman <gregkh@linuxfoundation.org> 2014-01-10 16:27:36 -0800
commit 2a41e6070dd7ef539d0f3b1652b4839d04378e11 (patch)
tree 9cb329a1231a1f971e6c18ff472090872adb8da2 /drivers/base/component.c
parent d1ba277e79889085a2faec3b68b91ce89c63f888 (diff)
download linux-2a41e6070dd7ef539d0f3b1652b4839d04378e11.tar.gz
diff --git a/drivers/base/component.c b/drivers/base/component.c
new file mode 100644
index 0000000000000..c53efe6c6d8eb
--- /dev/null
+++ b/drivers/base/component.c
组件式代码架构,由设备树代码和驱动代码组成。
- 设备树代码
在设备树文件中定义一个master设备结点,以master设备结点为核心,规划和其他组件之间的 连接关系,形成一个完整的图显系统拓扑结构图。 - 驱动代码
master负责绑定各个component组件,当所有组件均匹配、绑定成功后,DRM图显系统才能正常工作。任何一个组件或master结点被移除,都会引起解绑定事件并将subsystem的状态设置为down。
2. component组件
component组件的驱动程序执行probe时:
- 通过component_add()加入组件系统
- 通过component_del()退出组件系统
后面仅介绍component如何加入组件系统。
component驱动程序probe的最后阶段,向组件系统注册。提供master驱动程序绑定该组件时所需的回调函数,使用struct component_ops完成数据封装:
struct component_ops {
int (*bind)(struct device *comp, struct device *master,
void *master_data);
void (*unbind)(struct device *comp, struct device *master,
void *master_data);
};
- (*bind)
component的绑定函数,注册ENCODER/CONECTOR等 - (*unbind)
component的解绑定函数,释放ENCODER/CONECTOR等
component绑定时,通常不会涉及到组件设备的寄存器配置。因此,组件设备已经完成了注册但工作不正常时,不用排查绑定过程是否有问题。
component_ops例化:
static const struct component_ops dsi_ops = {
.bind = dsi_bind,
.unbind = dsi_unbind,
};
当master注册到组件系统时,通过component_bind_all()/component_unbind_all()来回调上面例化的API。
static int kirin_drm_kms_init(struct drm_device *dev,
const struct kirin_drm_data *driver_data)
{
...
/* bind and init sub drivers */
ret = component_bind_all(dev->dev, dev);
if (ret) {
DRM_ERROR("failed to bind all component.\n");
goto err_private_cleanup;
}
...
err_unbind_all:
component_unbind_all(dev->dev, dev);
...
}
component使用component_add()实现向组件系统的注册
static int dsi_probe(struct platform_device *pdev)
{
...
ret = dsi_parse_dt(pdev, dsi);
if (ret)
return ret;
platform_set_drvdata(pdev, data);
return component_add(&pdev->dev, &dsi_ops);
}
component注册流程:
2. master组件
2.1 获取拓扑结构
kernel DRM架构下的图显系统的各个模块,按照crtc、plane、encoder、connector等进行抽象,在设备树文件中静态规划各个模块之间的连接关系。
master驱动程序通过of_graph_get_remote_node()来解析设备树文件,获取component设备树结点。
- 箭头1
代表master结点,通常为图显处理器 - 箭头2
代表master设备的端口。若图显处理器支持多个显示设备,此处可能会有多个port项,通过phandle标识。 - 箭头3
代表master设备端口所连接的远端端口。若component设备有多个端口,通过phandle标识。 - 箭头4
remote的数据类型为device_node,代表component设备树结点
of_graph_get_remote_node(),最初的定义在drivers/of/base.c中,后来由于base.c文件中代码越来越多,在2017年的6月1日由Rob Herring 在内核版本4.12.0 rc1中,将of_graph_xxx相关函数移动到了新创建的property.c里面。
获取拓扑结构流程:
2.2 匹配component的规则
常见的匹配规则有两种:
- 基于DRM官方API
使用这种方式的SoC厂商有ARM、STI、海思等 - 基于设备驱动
使用这种方式的SoC厂商有三星、rockchip、中兴等
两种匹配方式最终都会归口到component_match_add_release()这个函数。
以DRM官方的drm_of_component_match_add() API为例,总结component匹配规则的创建和应用。
master驱动在probe过程中,首先解析设备树查找其所连接的component,获取到图显系统拓扑结构。通过drm_of_component_match_add()来例化component_match数据结构。
drm_of_component_match_add()函数中的第三个参数是一个函数指针,需要驱动程序来实现钩子函数,像下面这样:
static int compare_of(struct device *dev, void *data)
{
return dev->of_node == data;
}
其中,dev->node代表master设备树结点。
2.3 master聚合component
component_master_add_with_match()按照匹配规则来查找component。通过遍历component_match链表,若master连接的component组件都有效可用,那么调用component_master_ops.bind来实现各个组件的聚合。此处,component_bind_all()回调各component的bind函数。
2.4 component的bind流程
在master聚合所有component的过程中,使用find_components()发现所有的component组件后,component_bind_all()调用component_bind(),完成component的bind
static int component_bind(struct component *component, struct master *master,
void *data)
{
int ret;
...
dev_dbg(master->dev, "binding %s (ops %ps)\n",
dev_name(component->dev), component->ops);
ret = component->ops->bind(component->dev, master->dev, data);
if (!ret) {
component->bound = true;
...
return ret;
}
上面内容,总结了master与component的注册和bind流程,kernel完成这些工作后,Linux系统中便可以看到crtc、plane、encoder等DRM模块的信息。
|