本文是基于rk3566/rk3568平台的LCD驱动对DRM component 框架进行分析。
一、背景
kernel中的component框架是为了subsystem能够按照一定的顺序初始化设备而提出的架构。由于subsystem中由较多设备模块组成,而内核加载每个模块时间不定,则需要component框架来保证需最后初始化的设备加载前,所需设备全部加载完毕。
二、component框架描述
在component中,包含两个基本概念,master和component。master是设备树中的“超级设备(superdevice)”,负责管理该超级设备下的普通设备。component是由master管理的普通设备,要先初始化。master在设备树中一般为xxx-subsystem节点,如display-subsystem。在节点下有ports属性,属性里存有该master应该关联的普通设备。如下:
display_subsystem: display-subsystem {
compatible = "rockchip,display-subsystem";
memory-region = <&drm_logo>, <&drm_cubic_lut>;
memory-region-names = "drm-logo", "drm-cubic-lut";
ports = <&vop_out>;
devfreq = <&dmc>;
route {
route_dsi0: route-dsi0 {
status = "okay";
logo,uboot = "logo.bmp";
logo,kernel = "logo_kernel.bmp";
logo,mode = "center";
charge_logo,mode = "center";
connect = <&vp0_out_dsi0>;
};
};
};
三、设备的初始化流程
master即超级设备,执行probe函数时使用component_master_add_with_match函数注册自己到component框架中。
component即普通设备,执行probe函数时使用component_add函数注册自己到component框架中。
两种流程先后顺序并无要求,可随意顺序。每一个设备加入到框架中,框架就尝试进行匹配,当master匹配上所有component后,会调用master的bind回调,开始按顺序进行初始化,保证了当所有子设备全部probe成功后再执行初始化操作。
注:记master设备为主设备,component设备为从设备。
1、master设备初始化流程
1)驱动匹配节点后,执行.probe 函数,即rockchip_drm_platform_probe函数,如下:
static int rockchip_drm_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct component_match *match = NULL;
int ret;
ret = rockchip_drm_platform_of_probe(dev);
#if !IS_ENABLED(CONFIG_DRM_ROCKCHIP_VVOP)
if (ret)
return ret;
#endif
match = rockchip_drm_match_add(dev);
if (IS_ERR(match))
return PTR_ERR(match);
ret = component_master_add_with_match(dev, &rockchip_drm_ops, match);
if (ret < 0) {
rockchip_drm_match_remove(dev);
return ret;
}
dev->coherent_dma_mask = DMA_BIT_MASK(64);
return 0;
}
2)初始化match对象,即将已知的驱动和总线上的设备上进行匹配,并将匹配上的设备加入到struct component_match结构中,如下:
static int __init rockchip_drm_init(void)
{
int ret;
num_rockchip_sub_drivers = 0;
#if IS_ENABLED(CONFIG_DRM_ROCKCHIP_VVOP)
ADD_ROCKCHIP_SUB_DRIVER(vvop_platform_driver, CONFIG_DRM_ROCKCHIP_VVOP);
#else
ADD_ROCKCHIP_SUB_DRIVER(vop_platform_driver, CONFIG_ROCKCHIP_VOP);
ADD_ROCKCHIP_SUB_DRIVER(vop2_platform_driver, CONFIG_ROCKCHIP_VOP2);
ADD_ROCKCHIP_SUB_DRIVER(rockchip_lvds_driver,
CONFIG_ROCKCHIP_LVDS);
ADD_ROCKCHIP_SUB_DRIVER(rockchip_dp_driver,
CONFIG_ROCKCHIP_ANALOGIX_DP);
ADD_ROCKCHIP_SUB_DRIVER(cdn_dp_driver, CONFIG_ROCKCHIP_CDN_DP);
ADD_ROCKCHIP_SUB_DRIVER(dw_hdmi_rockchip_pltfm_driver,
CONFIG_ROCKCHIP_DW_HDMI);
ADD_ROCKCHIP_SUB_DRIVER(dw_mipi_dsi_driver,
CONFIG_ROCKCHIP_DW_MIPI_DSI);
ADD_ROCKCHIP_SUB_DRIVER(inno_hdmi_driver, CONFIG_ROCKCHIP_INNO_HDMI);
ADD_ROCKCHIP_SUB_DRIVER(rockchip_tve_driver,
CONFIG_ROCKCHIP_DRM_TVE);
ADD_ROCKCHIP_SUB_DRIVER(rockchip_rgb_driver, CONFIG_ROCKCHIP_RGB);
#endif
ret = platform_register_drivers(rockchip_sub_drivers,
num_rockchip_sub_drivers);
if (ret)
return ret;
ret = platform_driver_register(&rockchip_drm_platform_driver);
if (ret)
goto err_unreg_drivers;
return 0;
err_unreg_drivers:
platform_unregister_drivers(rockchip_sub_drivers,
num_rockchip_sub_drivers);
return ret;
}
static struct component_match *rockchip_drm_match_add(struct device *dev)
{
struct component_match *match = NULL;
int i;
for (i = 0; i < num_rockchip_sub_drivers; i++) {
struct platform_driver *drv = rockchip_sub_drivers[i];
struct device *p = NULL, *d;
do {
d = bus_find_device(&platform_bus_type, p, &drv->driver,
(void *)platform_bus_type.match);
put_device(p);
p = d;
if (!d)
break;
device_link_add(dev, d, DL_FLAG_STATELESS);
component_match_add(dev, &match, compare_dev, d);
} while (true);
}
if (IS_ERR(match))
rockchip_drm_match_remove(dev);
return match ?: ERR_PTR(-ENODEV);
}
注:
1)在图1,会添加默认vop2_platform_driver、dw_mipi_dsi_driver、dw_hdmi_rockchip_pltfm_driver、rockchip_rgb_driver等驱动到rockchip_sub_drivers中。
2)在图2,通过bus_find_device函数遍历platform总线上的设备,并将与rockchip_sub_drivers中的驱动进行匹配,若匹配上则通过component_match_add函数加入match中(即设备与驱动匹配上的则为从设备)。相关函数的具体实现如下:
static inline void component_match_add(struct device *master,
struct component_match **matchptr,
int (*compare)(struct device *, void *), void *compare_data)
{
component_match_add_release(master, matchptr, NULL, compare,
compare_data);
}
void component_match_add_release(struct device *master,
struct component_match **matchptr,
void (*release)(struct device *, void *),
int (*compare)(struct device *, void *), void *compare_data)
{
struct component_match *match = *matchptr;
if (IS_ERR(match))
return;
if (!match) {
match = devres_alloc(devm_component_match_release,
sizeof(*match), GFP_KERNEL);
if (!match) {
*matchptr = ERR_PTR(-ENOMEM);
return;
}
devres_add(master, match);
*matchptr = match;
}
if (match->num == match->alloc) {
size_t new_size = match->alloc + 16;
int ret;
ret = component_match_realloc(master, match, new_size);
if (ret) {
*matchptr = ERR_PTR(ret);
return;
}
}
match->compare[match->num].compare = compare;
match->compare[match->num].release = release;
match->compare[match->num].data = compare_data;
match->compare[match->num].component = NULL;
match->num++;
}
3)初始化master对象,并将master对象加到masters链表,再将component_list链表上与master上通过以dev进行匹配,然后将匹配上的component设备加到master设备的match成员,最后执行master的bind函数以及执行component下的bind函数进行初始化。相关代码如下:
int component_master_add_with_match(struct device *dev,
const struct component_master_ops *ops,
struct component_match *match)
{
struct master *master;
int ret;
/* Reallocate the match array for its true size */
ret = component_match_realloc(dev, match, match->num);
if (ret)
return ret;
master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
master->dev = dev;
master->ops = ops;
master->match = match;
component_master_debugfs_add(master);
/* Add to the list of available masters. */
mutex_lock(&component_mutex);
list_add(&master->node, &masters);
ret = try_to_bring_up_master(master, NULL);
if (ret < 0)
free_master(master);
mutex_unlock(&component_mutex);
return ret < 0 ? ret : 0;
}
在上面对象初始化结束后后,下面通过try_to_bring_up_master先通过find_components将匹配的component加到master的match成员中,然后进行回调master和component 的bind函数。
static int try_to_bring_up_master(struct master *master,
struct component *component)
{
int ret;
dev_dbg(master->dev, "trying to bring up master\n");
if (find_components(master)) {
dev_dbg(master->dev, "master has incomplete components\n");
return 0;
}
if (component && component->master != master) {
dev_dbg(master->dev, "master is not for this component (%s)\n",
dev_name(component->dev));
return 0;
}
if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
return -ENOMEM;
/* Found all components */
ret = master->ops->bind(master->dev);
if (ret < 0) {
devres_release_group(master->dev, NULL);
if (ret != -EPROBE_DEFER)
dev_info(master->dev, "master bind failed: %d\n", ret);
return ret;
}
master->bound = true;
return 1;
}
static int find_components(struct master *master)
{
struct component_match *match = master->match;
size_t i;
int ret = 0;
/*
* Scan the array of match functions and attach
* any components which are found to this master.
*/
for (i = 0; i < match->num; i++) {
struct component_match_array *mc = &match->compare[i];
struct component *c;
dev_dbg(master->dev, "Looking for component %zu\n", i);
if (match->compare[i].component)
continue;
c = find_component(master, mc->compare, mc->data);
if (!c) {
ret = -ENXIO;
break;
}
dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master);
/* Attach this component to the master */
match->compare[i].duplicate = !!c->master;
match->compare[i].component = c;
c->master = master;
}
return ret;
}
static struct component *find_component(struct master *master,
int (*compare)(struct device *, void *), void *compare_data)
{
struct component *c;
list_for_each_entry(c, &component_list, node) {
if (c->master && c->master != master)
continue;
if (compare(c->dev, compare_data))
return c;
}
return NULL;
}
4)bind函数的执行,由下知:master的bind函数除了自身初始化外,其他主要是为了回调component的bind函数。
static int rockchip_drm_bind(struct device *dev)
{
...
ret = component_bind_all(dev, drm_dev);
...
}
int component_bind_all(struct device *master_dev, void *data)
{
struct master *master;
struct component *c;
size_t i;
int ret = 0;
WARN_ON(!mutex_is_locked(&component_mutex));
master = __master_find(master_dev, NULL);
if (!master)
return -EINVAL;
/* Bind components in match order */
for (i = 0; i < master->match->num; i++)
if (!master->match->compare[i].duplicate) {
c = master->match->compare[i].component;
ret = component_bind(c, master, data);
if (ret)
break;
}
if (ret != 0) {
for (; i > 0; i--)
if (!master->match->compare[i - 1].duplicate) {
c = master->match->compare[i - 1].component;
component_unbind(c, master, data);
}
}
return ret;
}
static int component_bind(struct component *component, struct master *master,
void *data)
{
int ret;
... //省略无关的代码
ret = component->ops->bind(component->dev, master->dev, data);
component->bound = true;
...//省略无关的代码
return ret;
}
static void component_unbind(struct component *component,
struct master *master, void *data)
{
WARN_ON(!component->bound);
component->ops->unbind(component->dev, master->dev, data);
component->bound = false;
/* Release all resources claimed in the binding of this component */
devres_release_group(component->dev, component);
}
static int dw_mipi_dsi_bind(struct device *dev, struct device *master,
void *data)
{
...
... //省略代码
}
注:
1)在component_bind_all函数中可知,若当前有component(从设备)尚未执行probe/初始化完毕,则在此时会bind失败,就会执行unbind回调。这就保证了会按顺序进行初始化且等待所有从设备全部probe成功后再执行初始化操作。
2)此处以dw_mipi_dsi_bind为component进行举例。也即对应component_bind函数中的component->ops->bind(component->dev, master->dev, data)。
2、component设备初始化流程
1)驱动匹配节点后,执行.probe 函数,即dw_mipi_dsi_probe函数,如下
static int dw_mipi_dsi_probe(struct platform_device *pdev)
{
...//省略无关代码
return component_add(&pdev->dev, &dw_mipi_dsi_ops);
}
static const struct component_ops dw_mipi_dsi_ops = {
.bind = dw_mipi_dsi_bind,
.unbind = dw_mipi_dsi_unbind,
};
2)通过component_add函数分配并初始化component对象,并添加到component_list链表中,最后调用try_to_bring_up_masters进行初始化component。
int component_add(struct device *dev, const struct component_ops *ops)
{
struct component *component;
int ret;
component = kzalloc(sizeof(*component), GFP_KERNEL);
if (!component)
return -ENOMEM;
component->ops = ops;
component->dev = dev;
dev_dbg(dev, "adding component (ops %ps)\n", ops);
mutex_lock(&component_mutex);
list_add_tail(&component->node, &component_list);
ret = try_to_bring_up_masters(component);
if (ret < 0) {
if (component->master)
remove_component(component->master, component);
list_del(&component->node);
kfree(component);
}
mutex_unlock(&component_mutex);
return ret < 0 ? ret : 0;
}
3)在try_to_bring_up_masters函数中先遍历masters链表,再在try_to_bring_up_master中通过find_components函数进行component和master的相互绑定,最后调用master的bind回调。
static int try_to_bring_up_masters(struct component *component)
{
struct master *m;
int ret = 0;
list_for_each_entry(m, &masters, node) {
if (!m->bound) {
ret = try_to_bring_up_master(m, component);
if (ret != 0)
break;
}
}
return ret;
}
static int try_to_bring_up_master(struct master *master,
struct component *component)
{
int ret;
dev_dbg(master->dev, "trying to bring up master\n");
if (find_components(master)) {
dev_dbg(master->dev, "master has incomplete components\n");
return 0;
}
if (component && component->master != master) {
dev_dbg(master->dev, "master is not for this component (%s)\n",
dev_name(component->dev));
return 0;
}
if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
return -ENOMEM;
/* Found all components */
ret = master->ops->bind(master->dev);
if (ret < 0) {
devres_release_group(master->dev, NULL);
if (ret != -EPROBE_DEFER)
dev_info(master->dev, "master bind failed: %d\n", ret);
return ret;
}
master->bound = true;
return 1;
}
static int find_components(struct master *master)
{
struct component_match *match = master->match;
size_t i;
int ret = 0;
/*
* Scan the array of match functions and attach
* any components which are found to this master.
*/
for (i = 0; i < match->num; i++) {
struct component_match_array *mc = &match->compare[i];
struct component *c;
dev_dbg(master->dev, "Looking for component %zu\n", i);
if (match->compare[i].component)
continue;
c = find_component(master, mc->compare, mc->data);
if (!c) {
ret = -ENXIO;
break;
}
dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master);
/* Attach this component to the master */
match->compare[i].duplicate = !!c->master;
match->compare[i].component = c;
c->master = master;
}
return ret;
}
//遍历component_list,将component和master进行匹配比较
static struct component *find_component(struct master *master,
int (*compare)(struct device *, void *), void *compare_data)
{
struct component *c;
list_for_each_entry(c, &component_list, node) {
if (c->master && c->master != master)
continue;
if (compare(c->dev, compare_data))
return c;
}
return NULL;
}
注:以上即为两层遍历,先遍历masters链表取出master再和component_list中的每个component进行比较。若当前的master与componet_list无匹配上,则取出masters上的下个master来匹配。直到匹配上后执行master的bind回调,也即执行component的bind回调。
四、总结
componet框架是用来确保在subsystem中存在较多个设备模块组成时由于加载的顺序初始化不一定,所以用来保证最后初始化的设备加载前,所需的设备全部加载完毕。机制:每个设备加入到框架中,框架就尝试进行匹配,当master匹配上所有component后,会调用master的bind回调,开始按顺序进行初始化,保证当所有子设备全部probe成功后再执行初始化操作
1、master设备的流程:
component_match_add-》component_master_add_with_match-》try_to_bring_up_master-》
master->ops->bind(master->dev)
2、componet设备的流程:
component_add-》try_to_bring_up_masters-》try_to_bring_up_master-》master->ops->bind
-》component->ops->bind(component->dev, master->dev, data)
3、重点函数说明:
1)find_components-》find_component
说明:find_component函数遍历component_list链表,通过之前的compare和data(即dev)来查找在component_list链表上匹配的component(即受此master管理的component设备)
2)try_to_bring_up_master
说明:会基于master查找component_list链表和该master匹配的,并且回调master的bind函数。
3)try_to_bring_up_masters
说明:两轮遍历,即:外层为masters链表,内层为component_list链表,只是为了进行master和component的匹配,然后后尝试去回调master的bind函数。
4)component_bind_all
说明:遍历master上的match列表,然后执行component_bind去执行回调。并且在此函数中进行了尝试执行bind回调,若有一个失败则会再执行component_unbind。