1. imx8 DRM 架构入门向导
设备树中的有个节点:
display-subsystem {
compatible = "fsl,imx-display-subsystem";
ports = <&lcdif_disp0>;
};
它是drm 框架中的master,负责将各个drm 组件管理起来。
1.1 drm 的master
imx-drm-core.c 是master 的实现:
static const struct component_master_ops imx_drm_ops = {
.bind = imx_drm_bind,
.unbind = imx_drm_unbind,
};
他负责把绑定所有drm 组件。
1.2 drm 的crtc & plane
lcdif-crtc.c
static const struct component_ops lcdif_crtc_ops = {
.bind = lcdif_crtc_bind,
.unbind = lcdif_crtc_unbind,
};
lcdif_crtc_bind实现了lcdif controller 层级的控制类API & plane。
1.3 drm encoder & connector
sec_mipi_dsim-imx.c
static int imx_sec_dsim_bind(struct device *dev, struct device *master,
void *data)
{
.........
encoder = &dsim_dev->encoder;
ret = imx_drm_encoder_parse_of(drm_dev, encoder, np);
if (ret)
return ret;
drm_encoder_helper_add(encoder, &imx_sec_dsim_encoder_helper_funcs);
...........
ret = drm_encoder_init(drm_dev, encoder,
&imx_sec_dsim_encoder_funcs,
DRM_MODE_ENCODER_DSI, dev_name(dev));
if (ret)
return ret;
.......
/* bind sec dsim bridge */
ret = sec_mipi_dsim_bind(dev, master, data, encoder, res, irq, pdata);
if (ret) {
............
return ret;
}
.............
}
static const struct component_ops imx_sec_dsim_ops = {
.bind = imx_sec_dsim_bind,
.unbind = imx_sec_dsim_unbind,
};
mipi-dsi 层级的控制api。sec_mipi_dsim_bind 是一个重要的函数,它里面将encoder 和 connector 进行绑定,同时将connector 和panel 进行了绑定。
1.4 drm panel
panel-simple.c or panel-xxx.c
static const struct drm_panel_funcs panel_simple_funcs = {
.disable = panel_simple_disable,
.unprepare = panel_simple_unprepare,
.prepare = panel_simple_prepare,
.enable = panel_simple_enable,
.get_modes = panel_simple_get_modes,
.get_timings = panel_simple_get_timings,
};
太熟悉了,好好看看就对了。
2. DRM 使能显示设备的API:
drm_atomic_helper_commit_modeset_enables
sec_mipi_dsim_bridge_enable 的调用连:
Workqueue: events deferred_probe_work_func
[ 3.991709] Call trace:
[ 3.991715] dump_backtrace+0x0/0x140
[ 3.991718] show_stack+0x14/0x20
[ 3.991725] dump_stack+0xb4/0x114
[ 3.991731] sec_mipi_dsim_bridge_enable+0x364/0x478
[ 3.991737] drm_atomic_bridge_enable+0x4c/0x68
[ 3.991742] drm_atomic_helper_commit_modeset_enables+0x11c/0x230
[ 3.991748] lcdif_drm_atomic_commit_tail+0x2c/0x68
[ 3.991752] commit_tail+0x9c/0x138
[ 3.991756] drm_atomic_helper_commit+0xc8/0x140
[ 3.991760] drm_atomic_commit+0x48/0x58
[ 3.991765] drm_client_modeset_commit_atomic.isra.0+0x174/0x1f8
[ 3.991768] drm_client_modeset_commit_force+0x58/0x190
[ 3.991774] drm_fb_helper_restore_fbdev_mode_unlocked+0x70/0xd0
[ 3.991778] drm_fb_helper_set_par+0x2c/0x58
[ 3.991783] fbcon_init+0x3a4/0x508
[ 3.991787] visual_init+0xac/0x100
[ 3.991791] do_bind_con_driver+0x1cc/0x3a0
[ 3.991794] do_take_over_console+0x13c/0x1f8
[ 3.991798] do_fbcon_takeover+0x6c/0xd8
[ 3.991801] fbcon_fb_registered+0xf8/0x108
[ 3.991809] register_framebuffer+0x1e8/0x330
[ 3.991812] __drm_fb_helper_initial_config_and_unlock+0x2d0/0x470
[ 3.991816] drm_fbdev_client_hotplug+0xd4/0x188
[ 3.991819] drm_fbdev_generic_setup+0x98/0x140
[ 3.991823] imx_drm_bind+0x100/0x150
[ 3.991826] try_to_bring_up_master+0x164/0x1c8
[ 3.991829] __component_add+0xa0/0x168
[ 3.991834] component_add+0x10/0x18
[ 3.991839] imx_sec_dsim_probe+0x4c/0x70
[ 3.991846] platform_drv_probe+0x50/0xa0
[ 3.991850] really_probe+0xd4/0x318
[ 3.991854] driver_probe_device+0x54/0xe8
[ 3.991861] __device_attach_driver+0x80/0xb8
[ 3.991867] bus_for_each_drv+0x74/0xc0
[ 3.991871] __device_attach+0xe8/0x148
[ 3.991878] device_initial_probe+0x10/0x18
[ 3.991884] bus_probe_device+0x90/0x98
[ 3.991888] deferred_probe_work_func+0x64/0x98
[ 3.991895] process_one_work+0x198/0x320
[ 3.991899] worker_thread+0x1f0/0x420
[ 3.991903] kthread+0x138/0x158
[ 3.991907] ret_from_fork+0x10/0x1c
对这一段代码进行描述下:
/* mipi dsi host needs to be registered before bridge attach, since:
* 1. Have Panel
* The 'mipi_dsi_host_register' will allocate a mipi_dsi_device
* if the dsi host node has a panel child node in DTB. And dsi
* host ->attach() will be called in panel's probe().
*
* 2. Have Bridge
* The dsi host ->attach() will be called through the below
* 'drm_bridge_attach()' which will attach next bridge in a
* chain.
*/
ret = mipi_dsi_host_register(&dsim->dsi_host);
if (ret) {
dev_err(dev, "Unable to register mipi dsi host: %d\n", ret);
return ret;
}
dsim->bridge = bridge;
bridge->driver_private = dsim;
bridge->funcs = &sec_mipi_dsim_bridge_funcs;
bridge->of_node = dev->of_node;
bridge->encoder = encoder;
/* attach sec dsim bridge and its next bridge if exists */
ret = drm_bridge_attach(encoder, bridge, NULL);
if (ret) {
dev_err(dev, "Failed to attach bridge: %s\n", dev_name(dev));
/* no bridge exists, so defer probe to wait
* panel driver loading
*/
if (ret != -EPROBE_DEFER) {
for_each_available_child_of_node(dev->of_node, node) {
/* skip nodes without reg property */
if (!of_find_property(node, "reg", NULL))
continue;
/* error codes only ENODEV or EPROBE_DEFER */
dsim->panel = of_drm_find_panel(node);
if (!IS_ERR(dsim->panel))
goto panel;
ret = PTR_ERR(dsim->panel);
}
}
mipi_dsi_host_unregister(&dsim->dsi_host);
return ret;
}
panel:
if (dsim->panel) {
如果存在panel,第一次调用drm_bridge_attach会失败,因为没有子bridge,会返回EPROBE_DEFER的错误,第二次probe,这时panel已经attach, drm_bridge_attach 会调用成功,从而完成sec_mipi_dsim桥与encoder的attach。
那么panel 是何时attach 的呢?
当该函数调用mipi_dsi_host_register 时,产生了一个device注册进内核,该device 会和panel-simple.c 中的panel_simple_dsi_driver 进行匹配,从而调用他的probe 函数:
static int panel_simple_dsi_probe(struct mipi_dsi_device *dsi)
{
.......
err = mipi_dsi_attach(dsi);
if (err) {
struct panel_simple *panel = dev_get_drvdata(&dsi->dev);
drm_panel_remove(&panel->base);
}
return err;
}
mipi_dsi_attach 又会回掉原来host api 的attach 函数sec_mipi_dsim_host_attach。让我们来看下这个函数的实现:
static int sec_mipi_dsim_host_attach(struct mipi_dsi_host *host,
struct mipi_dsi_device *dsi)
{
......
if (!dsim->next) {
/* 'dsi' must be panel device */
panel = of_drm_find_panel(dsi->dev.of_node);
if (!panel) {
dev_err(dev, "refuse unknown dsi device attach\n");
WARN_ON(!panel);
return -ENODEV;
}
/* Don't support multiple panels */
if (dsim->panel && panel && dsim->panel != panel) {
dev_err(dev, "don't support multiple panels\n");
return -EBUSY;
}
dsim->panel = panel;
}
........
}
在这里为dsim->panel设定了直。