linux display软件总体框图
对于linux diplay软件框架的分析,我认为,应该带着如下几个疑问去阅读代码:
- /dev/fb, /dev/dri/card, dev/dri/controlD, dev/dri/renderD这些设备是如何生成的?
- DTS中定义的有关ARM HDLCD与HDMI的信息,在内核代码中是如何被使用到的?
- /dev/fb设备与ARM HDLCD控制器硬件是如何关联的?
- ARM HDLCD控制器是如何与HDMI芯片TD998X关联起来?
- HDMI芯片是如何通过I2C初始化的?
- 一帧YUV转换后得到的RGB数据,是如何通过fb到达crtc,再到encoder, connector,最后,通过HDMI线到达显示器,并且在显示器上面显示出来的?
- 为什么DRM框架会取代以往内核中非常普遍使用的Frame Buffer这种框架的?DRM到底有什么好处?
- DRM框架中用到了component机制,这个机制是干什么用的?
- DRM框架中KMS、GEM这些概念又是干什么用的?
- DRM框架是如何对GPU硬件进行支持的?
需要重点理解的核心数据结构
struct drm_device
struct drm_driver
struct drm_master
struct drm_framebuffer
struct drm_plane
struct drm_crtc
struct drm_encoder
const struct drm_encoder_funcs *funcs;
const struct drm_encoder_helper_funcs *helper_private;
xxx_helper_funcs里面通常是一些比较common的函数,而xxx_funs通常是一些与特定硬件相关的函数,因此,xxx_heler_funcs可能会调用xxx_funcs中的函数
struct drm_connector
static const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = {
.get_modes = tda998x_connector_get_modes,
.best_encoder = tda998x_connector_best_encoder,
};
static const struct drm_connector_funcs tda998x_connector_funcs = {
.reset = drm_atomic_helper_connector_reset,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = tda998x_connector_detect, //检测是否HDMI插入,读取寄存器cec_read(priv, REG_CEC_RXSHPDLEV) 的bit1是否为1
.destroy = tda998x_connector_destroy,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
};
struct drm_client_funcs
static const struct drm_client_funcs drm_fbdev_client_funcs = {
.owner = THIS_MODULE,
.unregister = drm_fbdev_client_unregister,
.restore = drm_fbdev_client_restore,
.hotplug = drm_fbdev_client_hotplug,
};
hdlcd_drm_bind
-> drm_fbdev_generic_setup(drm, 32)
-> drm_client_init(dev, &fb_helper->client, "fbdev", &drm_fbdev_client_funcs)
-> drm_fbdev_client_hotplug(&fb_helper->client)
-> drm_fb_helper_init(dev, fb_helper, 0)
-> dev->fb_helper = helper
-> drm_fb_helper_initial_config(fb_helper, fb_helper->preferred_bpp)
-> __drm_fb_helper_initial_config_and_unlock(fb_helper, bpp_sel) //启动时,第一次初始化
->drm_client_modeset_probe(&fb_helper->client, width, height) //这里读取EDID
-> drm_fb_helper_single_fb_probe(fb_helper, bpp_sel)
struct drm_fb_helper_funcs
static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
.fb_probe = drm_fbdev_cma_create,
};
display相关的dts定义
hdlcd_0
hdlcd@7ff50000 {
compatible = "arm,hdlcd";
reg = <0 0x7ff50000 0 0x1000>;
interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scmi_clk 3>;
clock-names = "pxlclk";
port {
hdlcd1_output: hdlcd1-endpoint {
remote-endpoint = <&tda998x_1_input>;
};
};
};
hdlcd_1
hdlcd@7ff60000 {
compatible = "arm,hdlcd";
reg = <0 0x7ff60000 0 0x1000>;
interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&scmi_clk 3>;
clock-names = "pxlclk";
port {
hdlcd0_output: hdlcd0-endpoint {
remote-endpoint = <&tda998x_0_input>;
};
};
};
tda998(hdmi0和hdmi1)
i2c@7ffa0000 {
compatible = "snps,designware-i2c";
reg = <0x0 0x7ffa0000 0x0 0x1000>; //I2C控制器基地址
#address-cells = <1>;
#size-cells = <0>;
interrupts = <GIC_SPI 104 IRQ_TYPE_LEVEL_HIGH>;
clock-frequency = <400000>; //i2c速率400K
i2c-sda-hold-time-ns = <500>;
clocks = <&soc_smc50mhz>;
hdmi_transmitter0: hdmi-transmitter@70 {
compatible = "nxp,tda998x";
reg = <0x70>; //I2C从设备地址
#sound-dai-cells = <1>;
audio-ports = <TDA998x_I2S 0x03
TDA998x_SPDIF 0x04>;
port {
tda998x_0_input: tda998x-0-endpoint {
remote-endpoint = <&hdlcd0_output>;
};
};
};
hdmi_transmitter1: hdmi-transmitter@71 {
compatible = "nxp,tda998x";
reg = <0x71>; //I2C从设备地址
port {
tda998x_1_input: tda998x-1-endpoint {
remote-endpoint = <&hdlcd1_output>;
};
};
};
};
arm hdlcd驱动分析
ARM HDLCD是在多个ARM开发平台上用到的显示控制器,由 ARM Ltd 提供。
HDLCD从帧缓冲区读取RGB数据并将其发送到数字编码器(DVI 或 HDMI)。
hdlcd控制器硬件说明
如下是juno文档中对HDLCD控制器的描述:
hdlcd控制器寄存器定义
//drivers/gpu/drm/arm/hdlcd_regs.h
/* register offsets */
#define HDLCD_REG_VERSION 0x0000 /* ro */
#define HDLCD_REG_INT_RAWSTAT 0x0010 /* rw */
#define HDLCD_REG_INT_CLEAR 0x0014 /* wo */
#define HDLCD_REG_INT_MASK 0x0018 /* rw */
#define HDLCD_REG_INT_STATUS 0x001c /* ro */
#define HDLCD_REG_FB_BASE 0x0100 /* rw */
#define HDLCD_REG_FB_LINE_LENGTH 0x0104 /* rw */
#define HDLCD_REG_FB_LINE_COUNT 0x0108 /* rw */
#define HDLCD_REG_FB_LINE_PITCH 0x010c /* rw */
#define HDLCD_REG_BUS_OPTIONS 0x0110 /* rw */
#define HDLCD_REG_V_SYNC 0x0200 /* rw */
#define HDLCD_REG_V_BACK_PORCH 0x0204 /* rw */
#define HDLCD_REG_V_DATA 0x0208 /* rw */
#define HDLCD_REG_V_FRONT_PORCH 0x020c /* rw */
#define HDLCD_REG_H_SYNC 0x0210 /* rw */
#define HDLCD_REG_H_BACK_PORCH 0x0214 /* rw */
#define HDLCD_REG_H_DATA 0x0218 /* rw */
#define HDLCD_REG_H_FRONT_PORCH 0x021c /* rw */
#define HDLCD_REG_POLARITIES 0x0220 /* rw */
#define HDLCD_REG_COMMAND 0x0230 /* rw */
#define HDLCD_REG_PIXEL_FORMAT 0x0240 /* rw */
#define HDLCD_REG_RED_SELECT 0x0244 /* rw */
#define HDLCD_REG_GREEN_SELECT 0x0248 /* rw */
#define HDLCD_REG_BLUE_SELECT 0x024c /* rw */
/* version */
#define HDLCD_PRODUCT_ID 0x1CDC0000
#define HDLCD_PRODUCT_MASK 0xFFFF0000
#define HDLCD_VERSION_MAJOR_MASK 0x0000FF00
#define HDLCD_VERSION_MINOR_MASK 0x000000FF
hdlcd_probe
drivers/gpu/drm/arm/hdlcd_drv.c
static struct platform_driver hdlcd_platform_driver = {
.probe = hdlcd_probe,
.remove = hdlcd_remove,
.driver = {
.name = "hdlcd",
.pm = &hdlcd_pm_ops,
.of_match_table = hdlcd_of_match,
},
};
static const struct of_device_id hdlcd_of_match[] = {
{ .compatible = "arm,hdlcd" },
{},
};
hdlcd_probe
->of_graph_get_next_endpoint(pdev->dev.of_node, NULL)
->port = of_get_child_by_name(parent, "port") //查找port结点
->endpoint = of_get_next_child(port, prev) //查找port中的endpoint结点
->port = of_graph_get_remote_port_parent(ep)
->np = of_parse_phandle(node, "remote-endpoint", 0) //解析得到"remote-endpoint"属性值:tda998x_1_input
->component_match_add(&pdev->dev, &match, compare_dev, port)
->component_match_add_release(master, matchptr, NULL, compare, compare_data)
-> match = devres_alloc(devm_component_match_release, sizeof(*match), GFP_KERNEL)
-> match->compare[match->num].compare = compare
->component_master_add_with_match(&pdev->dev, &hdlcd_master_ops, match) //hdlcd_master_ops定义在下面,里面有bind()接口
-> try_to_bring_up_master(master, NULL)
compare_dev定义如下:
static int compare_dev(struct device *dev, void *data)
{
return dev->of_node == data;
}
hdlcd_master_ops定义在下面,里面有bind()接口
static const struct component_master_ops hdlcd_master_ops = {
.bind = hdlcd_drm_bind,
.unbind = hdlcd_drm_unbind,
};
hdlcd_drm_bind
static const struct component_master_ops hdlcd_master_ops = {
.bind = hdlcd_drm_bind,
.unbind = hdlcd_drm_unbind,
};
hdlcd_drm_bind
->drm_dev_alloc(&hdlcd_driver, dev) // hdlcd_driver定义如下
->hdlcd_setup_mode_config(drm);
->drm->mode_config.funcs = &hdlcd_mode_config_funcs //定义在下面
->hdlcd_load(drm, 0);
->hdlcd->mmio = devm_ioremap_resource(drm->dev, res)
->version = hdlcd_read(hdlcd, HDLCD_REG_VERSION) //读hdlcd version寄存器
->of_reserved_mem_device_init(drm->dev)
->hdlcd_setup_crtc(drm)
->hdlcd_plane_init(drm)
->drm_plane_helper_add(plane, &hdlcd_plane_helper_funcs) // 里面有一个atomic_update()接口,会配置hdlcd的FB BASE寄存器
-> hdlcd_write(hdlcd, HDLCD_REG_FB_BASE, scanout_start)
-> drm_crtc_init_with_planes(drm, &hdlcd->crtc, primary, NULL, &hdlcd_crtc_funcs, NULL)
->drm_crtc_helper_add(&hdlcd->crtc, &hdlcd_crtc_helper_funcs)
->drm_irq_install(drm, platform_get_irq(pdev, 0))
->request_irq(irq, dev->driver->irq_handler, sh_flags, dev->driver->name, dev)
->drm_dev_register(drm, 0); //注册drm字符设备
->drm_minor_register(dev, DRM_MINOR_CONTROL);
->drm_minor_register(dev, DRM_MINOR_RENDER);
->drm_minor_register(dev, DRM_MINOR_PRIMARY);
->dev->driver->load(dev, flags) // hdlcd_driver中没有定义load()接口,所以这里为空
->drm_modeset_register_all(dev)
->drm_plane_register_all(dev); //调用各自的 late_register()接口
->drm_crtc_register_all(dev);
->drm_encoder_register_all(dev); //遍历dev->mode_config.encoder_list