一,前言
昨天的framebuffer驱动qemu仿真调试--Apple的学习笔记中我提及到vxpress v9仿真使用的是drm不是framebuffer,所以今天来说说drm。
二,DRM框架
关于mesa,opengl和vulkan的3D绘图我之前都做过。大总结:嵌入式3D动画学习步骤总结--Apple的学习笔记里面有好几篇drm相关的。但是没有从内核驱动角度去分析,我当时主要学习的是3D应用编程。所以我先查了下内核help文档,并且网上搜索了下drm源码分析的资料,大概了解了下框架及drm_driver和相关主要函数。
之前学习framebuffer,我直接用下图左边的mmap操控显示。以前学习opengl等的时候使用的是下图右边绿色的通路操控显示。这就是区别,然后除了drm还有KMS,但是KMS我还不太清楚,所以先忽略。
自己先画一个层次图,理清下模块关系。
我现在理解DRM相关模块含义
1.framebuffer依然是画布。
2.CRTC是显示模式[等价于fb_info构造的fbdev](panel是显示区 域)。
3.Decoder是解码算法(比如yuv,rgb等)
4.Connector是连接接口转换波形用的(比如HDMI或DVI,他们的接口及显示时序不同吧!)
三,drm源码分析(Kernel 5.4.61)
搜索从设备树来定位使用的驱动代码sil,sii9022
,立即搜索可以定位到sii902x_dt_ids.c而sii9022是HDMI解码芯片,它是一个i2c总线设备,不看具体内容,从框架角度来理解是比较容易的。
看了下设备树中endpoint的帮助,通过帮助还了解到了cma大容量内存设置也在设备树完成。
但是问题来了搜索关键字arm,pl111
的时候,是在pl111_vexpress_clcd_init(pl111_vexpress.c)函数内,对应的c文件都没有probe函数的,感觉不太对呢!自己大概的看下相关c代码,我猜测应该会进入pl111_amba_probe(pl111_drv.c)函数。然后看到了,经过一路我不太熟悉的API调用后,会调用到register_framebuffer函数
drm_fb_helper_initial_config
-->__drm_fb_helper_initial_config_and_unlock
---->register_framebuffer
还是调试下吧!主要是看看probe是怎么进入的。
因为pl111_drv.c中只看到match table,而且没有name,只有id。
static const struct amba_id pl111_id_table[] = {
{
.id = 0x00041110,
.mask = 0x000fffff,
.data = (void *)&pl110_variant,
},
{
.id = 0x00180110,
.mask = 0x00fffffe,
.data = (void *)&pl110_nomadik_variant,
},
{
.id = 0x00041111,
.mask = 0x000fffff,
.data = (void *)&pl111_variant,
},
{0, 0},
};
四,drm驱动调试
果然可以进入pl111_amba_probe函数,我猜测正确,但是如何match的呢?
关于match函数一般都在bus相关的函数中,我找到了amba bus注册amba_driver_register,然后看到了关键函数
amba_match
struct bus_type amba_bustype = {
.name = "amba",
.dev_groups = amba_dev_groups,
.match = amba_match,
.uevent = amba_uevent,
.dma_configure = platform_dma_configure,
.pm = &amba_pm,
};
match函数中会调用amba_lookup
,然后我看到了table->id和table->mask,我觉得和pl111_drv.c中的pl111_id_table对应上了,但是periphid是怎么来的呢?
static const struct amba_id *
amba_lookup(const struct amba_id *table, struct amba_device *dev)
{
while (table->mask) {
if (((dev->periphid & table->mask) == table->id) &&
((dev->cid != CORESIGHT_CID) ||
(amba_cs_uci_id_match(table, dev))))
return table;
table++;
}
return NULL;
}
源码中搜索了下关键字periphid
,猜测就是通过如下赋值的。
static int amba_device_try_add(struct amba_device *dev, struct resource *parent)
{
......
/*
* Read pid and cid based on size of resource
* they are located at end of region
*/
for (pid = 0, i = 0; i < 4; i++)
pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) <<
(i * 8);
for (cid = 0, i = 0; i < 4; i++)
cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) <<
(i * 8);
if (cid == CORESIGHT_CID) {
/* set the base to the start of the last 4k block */
void __iomem *csbase = tmp + size - 4096;
dev->uci.devarch =
readl(csbase + UCI_REG_DEVARCH_OFFSET);
dev->uci.devtype =
readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff;
}
amba_put_disable_pclk(dev);
if (cid == AMBA_CID || cid == CORESIGHT_CID) {
dev->periphid = pid; //periphid是在此处赋值的
dev->cid = cid;
}
}
然后调试验证下,谜底揭开了。果然是在此处赋值的,每次都是赋值新的值。通过函数内的注释大概了解到这些数值是资源的地址范围。此款芯片我不了解,暂时也不去下载datasheet查看,本次目的主要是顺便学习内核drm子系统框架,至于具体的drm内核API及详细代码将来有空再研究。
五,总结
了解了drm框架中的主要模块及关系。验证了pl111 LCDC驱动的probe是通过id经过mask匹配资源地址成功后进入的。另外了解下基于drm的设备树的关键字,今天算是drm驱动入门了。