本文将从以下几个方面来学习研究mipi_dsi总线
- mipi-dsi总线的注册
- 总线的匹配规则
- dsi控制器的注册
- mipi_dsi总线设备的解析与生成
- dsi控制器驱动示例
- mipi-dsi总线驱动的注册
mipi-dsi总线的注册
源文件:kernel/driver/gpu/drm/drm_mipi_dsi.c
c
static int __init mipi_dsi_bus_init(void)
{
return bus_register(&mipi_dsi_bus_type);
}
postcore_initcall(mipi_dsi_bus_init);
c
static struct bus_type mipi_dsi_bus_type = {
.name = "mipi-dsi",
.match = mipi_dsi_device_match,
.uevent = mipi_dsi_uevent,
.pm = &mipi_dsi_device_pm_ops,
};
总线的匹配规则

这里的匹配规则比较简单与platform设备匹配规则 比较相似。
dsi控制器的注册
dis控制器注册使用函数module_mipi_dsi_driver来实现
mipi_dsi_host_register
int mipi_dsi_host_register(struct mipi_dsi_host *host)
{
struct device_node *node;
for_each_available_child_of_node(host->dev->of_node, node) {
/* skip nodes without reg property */
if (!of_find_property(node, "reg", NULL))
continue;
of_mipi_dsi_device_add(host, node);
}
mutex_lock(&host_lock);
list_add_tail(&host->list, &host_list);
mutex_unlock(&host_lock);
return 0;
}
这个函数主要有两点:
- 解析控制器配置的总线设备,每个设备节点必须包含reg属性,不然不会生成相应的总线设备。
- 将控制器添加到host_list链表上
总线设备的解析与生成
of_mipi_dsi_device_add

mipi_dsi_device_register_full

dsi控制器驱动示例
控制器源码:linux-6.4\drivers\gpu\drm\sprd\sprd_dsi.c
c
static int sprd_dsi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct sprd_dsi *dsi;
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
return -ENOMEM;
dev_set_drvdata(dev, dsi);
dsi->host.ops = &sprd_dsi_host_ops;
dsi->host.dev = dev;
return mipi_dsi_host_register(&dsi->host);
}
此控制器代码简单明了,思路一目了然。
核心的回调函数如下:
static const struct mipi_dsi_host_ops sprd_dsi_host_ops = {
.attach = sprd_dsi_host_attach,
.detach = sprd_dsi_host_detach,
.transfer = sprd_dsi_host_transfer,
};
mipi-dsi总线驱动的注册
此总线的驱动注册一般使用宏module_mipi_dsi_driver进行驱动模块的加载,其定义如下:
c
int mipi_dsi_driver_register_full(struct mipi_dsi_driver *driver,
struct module *owner);
void mipi_dsi_driver_unregister(struct mipi_dsi_driver *driver);
#define mipi_dsi_driver_register(driver) \
mipi_dsi_driver_register_full(driver, THIS_MODULE)
#define module_mipi_dsi_driver(__mipi_dsi_driver) \
module_driver(__mipi_dsi_driver, mipi_dsi_driver_register, \
mipi_dsi_driver_unregister)
mipi_dsi_driver_register_full
int mipi_dsi_driver_register_full(struct mipi_dsi_driver *drv,
struct module *owner)
{
drv->driver.bus = &mipi_dsi_bus_type;
drv->driver.owner = owner;
if (drv->probe)
drv->driver.probe = mipi_dsi_drv_probe;
if (drv->remove)
drv->driver.remove = mipi_dsi_drv_remove;
if (drv->shutdown)
drv->driver.shutdown = mipi_dsi_drv_shutdown;
return driver_register(&drv->driver);
}
mipi-dsi总线驱动示例分析
源文件:linux-6.4\drivers\gpu\drm\panel\panel-asus-z00t-tm5p5-n35596.c
static struct mipi_dsi_driver tm5p5_nt35596_driver = {
.probe = tm5p5_nt35596_probe,
.remove = tm5p5_nt35596_remove,
.driver = {
.name = "panel-tm5p5-nt35596",
.of_match_table = tm5p5_nt35596_of_match,
},
};
tm5p5_nt35596_probe

下面一一对上述关键的函数进行解析:
- dsi->lanes = 4; 一般情况下dphy用的4lan
- dsi->format = MIPI_DSI_FMT_RGB888;格式设置,支持的格式如下
- enum mipi_dsi_pixel_format {
MIPI_DSI_FMT_RGB888,
MIPI_DSI_FMT_RGB666,
MIPI_DSI_FMT_RGB666_PACKED,
MIPI_DSI_FMT_RGB565,
};
- enum mipi_dsi_pixel_format {
drm_panel_init
void drm_panel_init(struct drm_panel *panel, struct device *dev,
const struct drm_panel_funcs *funcs, int connector_type)
{
if (connector_type == DRM_MODE_CONNECTOR_Unknown)
DRM_WARN("%s: %s: a valid connector type is required!\n", __func__, dev_name(dev));
INIT_LIST_HEAD(&panel->list);
panel->dev = dev;
panel->funcs = funcs;
panel->connector_type = connector_type;
}
static const struct drm_panel_funcs tm5p5_nt35596_panel_funcs = {
.prepare = tm5p5_nt35596_prepare,
.unprepare = tm5p5_nt35596_unprepare,
.get_modes = tm5p5_nt35596_get_modes,
};
struct drm_panel_funcs {
int (*prepare)(struct drm_panel *panel);
int (*enable)(struct drm_panel *panel);
int (*disable)(struct drm_panel *panel);
int (*unprepare)(struct drm_panel *panel);
int (*get_modes)(struct drm_panel *panel,
struct drm_connector *connector);
enum drm_panel_orientation (*get_orientation)(struct drm_panel *panel);
int (*get_timings)(struct drm_panel *panel, unsigned int num_timings,
struct display_timing *timings);
void (*debugfs_init)(struct drm_panel *panel, struct dentry *root);
};
- prepare 一般用于上电时序控制,以及dsi-cmd亮屏命令初始化
- enable 一般用于控制使用上电亮屏,此屏不涉及使用的ldo供电
- unprepare 一般用于下电时序控制,以及dsi-cmd灭屏命令的写入
- disable 一般用于控制使用下电灭屏,此屏不涉及使用的ldo供电
tm5p5_nt35596_create_backlight
static struct backlight_device *
tm5p5_nt35596_create_backlight(struct mipi_dsi_device *dsi)
{
struct device *dev = &dsi->dev;
const struct backlight_properties props = {
.type = BACKLIGHT_RAW,
.brightness = 255,
.max_brightness = 255,
};
return devm_backlight_device_register(dev, dev_name(dev), dev, dsi,
&tm5p5_nt35596_bl_ops, &props);
}
会调用linux提供的背光注册函数,不过高通和MTK都有自己的解决方案不般不会用这一块。这里只做参数,代码比较简单。将回调注册到背光链表上,后面有使用再进行操作。
源码位置:/kernel/driver/video/backlight/backlight.c

drm_panel_add
void drm_panel_add(struct drm_panel *panel)
{
mutex_lock(&panel_lock);
list_add_tail(&panel->list, &panel_list);
mutex_unlock(&panel_lock);
}
mipi_dsi_attach
int mipi_dsi_attach(struct mipi_dsi_device *dsi)
{
const struct mipi_dsi_host_ops *ops = dsi->host->ops;
int ret;
if (!ops || !ops->attach)
return -ENOSYS;
ret = ops->attach(dsi->host, dsi);
if (ret)
return ret;
dsi->attached = true;
return 0;
}
调用控制器的回调函数进行绑定,这里回过头来看上面注册的控制器的attach回调函数。
sprd_dsi_host_attach

int component_add(struct device *dev, const struct component_ops *ops)
{
return __component_add(dev, ops, 0);
}





本篇到这里就结束了,后面针对master再进行对应的学习和研究