学习Linux图形DRM子系统,还是需要有一个运行DRM框架的linux系统,这样无论在学习DRM应用程序还是驱动程序时,可以实际动手修改调试,运行看到效果,学习起来也是比较有动力和成就感的。下面是一个搭建Linux DRM运行环境的实践
QEMU + vexpress-a9 + linux-4.14.7 kernel
一、linux-4.14.7默认使用的显示驱动是framebuffer子系统,当使用QEMU图形界面运行时,可以直接以图形界面运行linux(framebuffer):
但是我们需要的是用DRM子系统来显示画面,因此就需要将framebuffer替换为DRM
二、从默认的framebuffer配置vexpress_defconfig可以得知,vexpress-a9开发板用的是ARM PrimeCell PL110 LCD controller,对应的fb驱动文件是amba-clcd.c(amba驱动)。对应的panel驱动文件是amba-clcd-versatile.c,负责和panel相关的初始化操作。对应的设备树节点如下:
clcd@10020000 {
compatible = "arm,pl111", "arm,primecell";
reg = <0x10020000 0x1000>;
interrupt-names = "combined";
interrupts = <0 44 4>;
clocks = <&oscclk1>, <&oscclk2>;
clock-names = "clcdclk", "apb_pclk";
max-memory-bandwidth = <130000000>; /* 16bpp @ 63.5MHz */
port {
clcd_pads: endpoint {
remote-endpoint = <&clcd_panel>;
arm,pl11x,tft-r0g0b0-pads = <0 8 16>;
};
};
panel {
compatible = "panel-dpi";
port {
clcd_panel: endpoint {
remote-endpoint = <&clcd_pads>;
};
};
panel-timing {
clock-frequency = <63500127>;
hactive = <1024>;
hback-porch = <152>;
hfront-porch = <48>;
hsync-len = <104>;
vactive = <768>;
vback-porch = <23>;
vfront-porch = <3>;
vsync-len = <4>;
};
};
};
三、在drivers/gpu/drm目录下找到pl111文件夹,标注是PL111 CLCD Controller,这个驱动应该就是PL111对应的DRM驱动:
linux-4.14.7/drivers/gpu/drm/pl111$ ls
built-in.o modules.order pl111_debugfs.c pl111_drm.h
Kconfig pl111_connector.c pl111_debugfs.o pl111_drm.o
Makefile pl111_connector.c_bak pl111_display.c pl111_drv.c
modules.builtin pl111_connector.o pl111_display.o pl111_drv.o
四、完成上述的配置并运行后,发现并没有生成dri/card0节点,查看开机的log,发现有如下报错:
drm-clcd-pl111 10020000.clcd: Disabling due to lack of DRM panel device.
drm-clcd-pl111: probe of 10020000.clcd failed with error -17
看样子是没有找到drm panel设备,应该是还缺少panel驱动文件。DRM中有panel有关的文件都放在drivers/gpu/drm/panel里
linux-4.14.7/drivers/gpu/drm/panel$ ls
built-in.o panel-innolux-p079zca.c panel-samsung-s6e8aa0.c
Kconfig panel-jdi-lt070me05000.c panel-sharp-lq101r1sx01.c
Makefile panel-lg-lg4573.c panel-sharp-ls043t1le01.c
modules.builtin panel-lvds.c panel-simple.c
modules.order panel-panasonic-vvx10f034n00.c panel-sitronix-st7789v.c
panel-samsung-ld9040.c panel-samsung-s6e3ha2.c
panel驱动文件主要是提供panel的一些操作函数,通过调用drm_panel_init生成drm_panel实例,然后调用drm_panel_add添加到DRM CORE中。
在上述文件里面并没有找到特别适合我们平台的panel文件,然后在更高版本的kernel中有找到panel-arm-versatile.c,看注释描述比较符合,因此将该文件作为平台的panel驱动文件
五、稍微修改panel驱动文件、connector驱动文件、设备树文件后,DRM就运行起来了。可以看到生成了card0节点:
/sys/class/drm # ls
card0 card0-DPI-1 version
使用图形界面运行,就发现可以正常显示linux图形界面了!
qemu-system-arm -M vexpress-a9 -m 512M -kernel ../zImage -dtb ./vexpress-v2p-ca9.dtb -append "root=/dev/mmcblk0 rw init=/linuxrc" -sd /home/neo/work/a9rootfs.ext3
附录:Doc in pl111_drv.c
/**
* DOC: ARM PrimeCell PL111 CLCD Driver
*
* The PL111 is a simple LCD controller that can support TFT and STN
* displays. This driver exposes a standard KMS interface for them.
*
* This driver uses the same Device Tree binding as the fbdev CLCD
* driver. While the fbdev driver supports panels that may be
* connected to the CLCD internally to the CLCD driver, in DRM the
* panels get split out to drivers/gpu/drm/panels/. This means that,
* in converting from using fbdev to using DRM, you also need to write
* a panel driver (which may be as simple as an entry in
* panel-simple.c).
*
* The driver currently doesn't expose the cursor. The DRM API for
* cursors requires support for 64x64 ARGB8888 cursor images, while
* the hardware can only support 64x64 monochrome with masking
* cursors. While one could imagine trying to hack something together
* to look at the ARGB8888 and program reasonable in monochrome, we
* just don't expose the cursor at all instead, and leave cursor
* support to the X11 software cursor layer.
*
* TODO:
*
* - Fix race between setting plane base address and getting IRQ for
* vsync firing the pageflip completion.
*
* - Expose the correct set of formats we can support based on the
* "arm,pl11x,tft-r0g0b0-pads" DT property.
*
* - Use the "max-memory-bandwidth" DT property to filter the
* supported formats.
*
* - Read back hardware state at boot to skip reprogramming the
* hardware when doing a no-op modeset.
*
* - Use the CLKSEL bit to support switching between the two external
* clock parents.
*/