通过前面两个系列的学习,我们已经了解DSS系统,LCD基本原理,DSS设备树的配置等基本知识。本文简单学习和梳理LCD设备驱动的代码,方便项目中快速bring up和debug。
此系列文章基于TI的AM572x EVM开发板,使用参考代码linux-4.14.67+gitAUTOINC+d315a9bb00-gd315a9bb00
1. 设备的枚举
我们知道linux设备驱动模型里面分为设备和驱动两部分,设备由device tree负责定义,内核代码会解析设备树枚举出设备;但在阅读代码的过程中会发现OMAP相关的LCD驱动代码有3部分,分别位于:
drivers/video/fbdev/omap/
drivers/video/fbdev/omap2/
drivers/gpu/drm/omapdrm/displays/
这3部分代码很类似,前面两个是基于fb,后面一个是基于drm,我们可以通过编译选项来确认到底用到哪部分代码。以config文件tisdk_am57xx-evm_defconfig为例,它定义了如下2个宏控。
CONFIG_MACH_OMAP_GENERIC
CONFIG_SOC_DRA7XX
我们找到machine的初始化代码arch/arm/mach-omap2/board-generic.c,通过上面的2个宏控找到对应的代码入口函数定义: DT_MACHINE_START
#ifdef CONFIG_SOC_DRA7XX
static const char *const dra74x_boards_compat[] __initconst = {
"ti,dra762",
"ti,am5728",
"ti,am5726",
"ti,dra742",
"ti,dra7",
NULL,
};
DT_MACHINE_START(DRA74X_DT, "Generic DRA74X (Flattened Device Tree)")
#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
.dma_zone_size = SZ_2G,
#endif
.reserve = omap_reserve,
.smp = smp_ops(omap4_smp_ops),
.map_io = dra7xx_map_io,
.init_early = dra7xx_init_early,
.init_late = dra7xx_init_late,
.init_irq = omap_gic_of_init,
.init_machine = omap_generic_init,
.init_time = omap5_realtime_timer_init,
.dt_compat = dra74x_boards_compat,
.restart = omap44xx_restart,
MACHINE_END
static const char *const dra72x_boards_compat[] __initconst = {
"ti,am5718",
"ti,am5716",
"ti,dra722",
"ti,dra718",
NULL,
};
DT_MACHINE_START(DRA72X_DT, "Generic DRA72X (Flattened Device Tree)")
#if defined(CONFIG_ZONE_DMA) && defined(CONFIG_ARM_LPAE)
.dma_zone_size = SZ_2G,
#endif
.reserve = omap_reserve,
.map_io = dra7xx_map_io,
.init_early = dra7xx_init_early,
.init_late = dra7xx_init_late,
.init_irq = omap_gic_of_init,
.init_machine = omap_generic_init,
.init_time = omap5_realtime_timer_init,
.dt_compat = dra72x_boards_compat,
.restart = omap44xx_restart,
MACHINE_END
#endif
其中的omap_generic_init中就包含了dss设备的初始化。其中的pdata_quirks_init函数将枚举platform设备,即dss@58000000 这个设备会被当成平台设备枚举出来;但是它的子设备如ports,dispc@58001000,encoder@58060000等则不会被枚举(参考of_platform_populate的实现)。接下来会调用omapdss_init_of();在omapdss_init_of()函数之中,再一次调用了of_platform_populate(),才将dss中的子设备建立起来。再然后调用omapdss_init_fbdev()进行fb设备的初始化,不过我们可以看到由于没有定义CONFIG_FB_OMAP2,这个函数实现为空。omapdss_init_fbdev()当中会创建fb相关的设备,对应的驱动在drivers/video/fbdev/omap2/omapfb/dss中实现,由于没有枚举设备,对应的驱动自然也不会被加载。当然通过宏的设置也可以确认对于当前的配置,我们使用的代码位于drivers/gpu/drm/omapdrm/displays/。
2. 驱动的加载
既然已经知道dss驱动使用的是drm中,可以很容易找到初始化的地方,位于文件drivers/gpu/drm/omapdrm/dss/dss.c。
/* INIT */
static struct platform_driver * const omap_dss_drivers[] = {
&omap_dsshw_driver,
&omap_dispchw_driver,
#ifdef CONFIG_OMAP2_DSS_DSI
&omap_dsihw_driver,
#endif
#ifdef CONFIG_OMAP2_DSS_VENC
&omap_venchw_driver,
#endif
#ifdef CONFIG_OMAP4_DSS_HDMI
&omapdss_hdmi4hw_driver,
#endif
#ifdef CONFIG_OMAP5_DSS_HDM