最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

Linux操作系统更改显示屏分辨率底层驱动实现原理 | 基于kernel DRM KMS

XAMPP相关 admin 88浏览 0评论

1. 引言

在之前的文章中,更多的是介绍DRM各组件的初始化流程、注册方式、管理机制等相关内容。

通过前面的文章大家可以了解到DRM component以及bind机制,图显处理器、HDMI、MIPI DSI、LVDS等图显IP初始化流程,如何注册出下图所示的DRM各个组件。

初始化工作并不是结束而仅仅是一个开始,此时图显IP已经处于准备就绪的状态,最终要按照用户需求来工作。

在后面文章中,我们会着重介绍DRM架构如何高效、便捷的支撑用户来管理和使用图显设备。

与用户层相关的内容当中,最重要的当属KMS(Kernel mode-setting)GEM*(Graphics Execution Manager)。KMS是保证图显IP正常工作的前提,它涉及到图显系统的参数配置,因此,我们首先介绍KMS相关内容。

2. 啥是KMS?为啥需要

图显系统在实际使用过程中,一定要按照用户指定的模式工作并达到预期的显示效果,以满足不同用户的喜好,适配不同的应用场景。

用户会提交各种各样的图显系统的模式配置信息,比如屏幕分辨率以及色彩属性等均属于模式配置的范畴。

在没有引入KMS之前,由于图显系统涉及到图显外设、fb、DRM core、用户空间X服务、用户空间DRI等模块,任何一个模块都可能引入故障,所以,实现图显系统(显卡)的模式配置是非常难并且有较大的失败风险。

例如,当Linux系统起来后,X.org已经完成了初始化,按照X.org的规则配置显卡,此时它就可以操控底层的硬件设备。但是,当我们从X.org切换到了VT console之后,需要按照VT console的规则重新初始化图显系统,这种附加的工作量极易引入软件层面的bug。

另外一方面我们要知道,模式配置是在屏幕正常工作的情况下进行的,图像显示是一种动态行为,而我们的模式配置需要在不打断屏幕当前工作的前提下,将用户最新的配置信息提交给硬件设备。因此,我们必须保证参数的正确性并且显示效果不受影响,只能成功,不能失败。

基于此,DRM架构下必须实现一套完美的模式配置和图显设备管理的流程及方式,而它,被我们称之为KMS。

kernel 2.6.29引入了KMS机制:

committer	Dave Airlie <airlied@linux.ie>	2008-12-29 17:47:23 +1000
commit f453ba0460742ad027ae0c4c7d61e62817b3e7ef (patch)
tree 29e6ecacd6e8971aa62e1825d77f2c1876ac3eb2
parent de151cf67ce52ed2d88083daa5e60c7858947329 (diff)
download linux-f453ba0460742ad027ae0c4c7d61e62817b3e7ef.tar.gz
DRM: add mode setting support
Add mode setting support to the DRM layer.

This is a fairly big chunk of work that allows DRM drivers to provide
full output control and configuration capabilities to userspace. It was
motivated by several factors:
- the fb layer's APIs aren't suited for anything but simple
configurations
- coordination between the fb layer, DRM layer, and various userspace
drivers is poor to non-existent (radeonfb excepted)
- user level mode setting drivers makes displaying panic & oops
messages more difficult
- suspend/resume of graphics state is possible in many more
configurations with kernel level support

This commit just adds the core DRM part of the mode setting APIs.
Driver specific commits using these new structure and APIs will follow.

Co-authors: Jesse Barnes <jbarnes@virtuousgeek.org>, Jakob Bornecrantz <jakob@tungstengraphics.com>
Contributors: Alan Hourihane <alanh@tungstengraphics.com>, Maarten Maathuis <madman2003@gmail.com>

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
Signed-off-by: Dave Airlie <airlied@redhat.com>

3. KMS机制及初始化

3.1 KMS实现机制

我们都知道图显处理器、图显外设等都属于显卡的范畴,在DRM架构下通过struct drm_device代表显卡设备。对于KMS而言,需要DRM master在初始化过程中调用drm_mode_config_init()函数,初始化struct drm_device中的mode_config成员以及注册用户空间所需的API接口。

/**
 * DRM device structure. This structure represent a complete card that
 * may contain multiple heads.
 */

struct drm_device {
 struct list_head legacy_dev_list;
 int if_version;
......
 struct drm_mode_config mode_config; /**< Current mode config */
......
};

查看kernel代码可以发现各SoC厂商的图显系统驱动代码均调用了drm_mode_config_init()

rk@ubuntu:~/OK3399-linux-release/kernel$ grep -Rn "drm_mode_config_init" drivers/gpu/drm/
drivers/gpu/drm/rcar-du/rcar_du_kms.c:750: drm_mode_config_init(dev);
drivers/gpu/drm/amd/amdgpu/amdgpu_device.c:1511: drm_mode_config_init(adev->ddev);
drivers/gpu/drm/cirrus/cirrus_mode.c:575: drm_mode_config_init(cdev->dev);
drivers/gpu/drm/rockchip/rockchip_drm_fb.h:25:void rockchip_drm_mode_config_init(struct drm_device *dev);
drivers/gpu/drm/rockchip/rockchip_drm_fb.c:406:void rockchip_drm_mode_config_init(struct drm_device *dev)
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:1451: drm_mode_config_init(drm_dev);
drivers/gpu/drm/rockchip/rockchip_drm_drv.c:1453: rockchip_drm_mode_config_init(drm_dev);
Binary file drivers/gpu/drm/rockchip/rockchipdrm.o matches
Binary file drivers/gpu/drm/rockchip/rockchip_drm_drv.o matches
.....
drivers/gpu/drm/drm_crtc.c:6193:void drm_mode_config_init(struct drm_device *dev)
drivers/gpu/drm/drm_crtc.c:6222:EXPORT_SYMBOL(drm_mode_config_init);
......
rk@ubuntu:~/OK3399-linux-release/kernel$ 

我们在上面的代码片段中发现drm_mode_config_init()的定义位于drm_crtc.c中,那这是为什么呢?

在了解这个原因之前,先回顾一下DRM图显系统的数据流。

图显系统的数据流概况为fb->plane->crtc->encoder->connector,如下图所示:

首先由用户层申请framebuffer内存,将图形图像数据按照特定的像素格式填充进内存空间。对于支持多图层的图显处理器而言,它们均具备多个plane,我们可以指定framebuffer和plane之间的绑定关系,也就是说指定plane从哪个framebuffer中获取到图显数据。当我们的图显系统也就是显卡可以支持多屏显示时,需要多个crtc组件,该图中仅列出1个,代表当前图显系统在同一时刻仅支持一个显示屏,像HDMI、VGA等显示屏均支持热插拔功能,因此仅具备1个crtc同样能够丰富我们的应用场景。

从CRTC作为DRM时序控制组件以及图显数据流中所处的位置这两点也不难发现,其承担DRM的模式配置功能也就顺利成章了。因此,drm_mode_config_init()需要由drm_crtc实现。

3.2 KMS的初始化

关于DRM KMS的初始化需要了解以下三点:

  • 何时初始化
  • 初始化哪些资源
  • 注册哪些KMS的API接口函数

本文依然是以RK3399为例
内核版本是4.4.189

「何时初始化」
从前文说明中可知,由DRM master驱动完成drm_mode_config_init()的调用,对于RK3399而言,master设备为DRM device,并不是其图显处理器VOP。

static int rockchip_drm_bind(struct device *dev)
{
 struct drm_device *drm_dev;
......
 drm_dev = drm_dev_alloc(&rockchip_drm_driver, dev);
 if (!drm_dev)
  return -ENOMEM;
......
 drm_mode_config_init(drm_dev);
 rockchip_drm_mode_config_init(drm_dev);
 rockchip_drm_create_properties(drm_dev);
 /* Try to bind all sub drivers. */
 ret = component_bind_all(dev, drm_dev);
......
}

以上代码表明,drm_mode_config_init()的执行时间点非常靠前。介于DRM device初始化之后,各图显IP初始化之前。前者提供了KMS的主体,后者需要基于此例化KMS的各种资源。

「初始化哪些资源」

初始化资源

前三点通过调用drm_mode_config_init()实现。具体如下:

void drm_mode_config_init(struct drm_device *dev)
{
 mutex_init(&dev->mode_config.mutex);
......
 INIT_LIST_HEAD(&dev->mode_config.fb_list);
......
 drm_modeset_lock_all(dev);
 drm_mode_create_standard_properties(dev);
 drm_modeset_unlock_all(dev);
......
}

此处提到了DRM property,它是支撑KMS能够实现原子操作的前提,在此处不做展开描述,后面会针对DRM property做专门介绍。

为了支持DRM KMS 模式配置自适应以及由于硬件IP的特性限制,需要对分辨率以及framebuffer阈值进行初始化。当用户进行模式配置时,首先要检查用户提交的信息是否合法,而判定依据即来源于此。

 dev->mode_config.min_width = 0;
 dev->mode_config.min_height = 0;

 /*
  * set max width and height as default value(4096x4096).
  * this value would be used to check framebuffer size limitation
  * at drm_mode_addfb().
  */

 dev->mode_config.max_width = 8192;
 dev->mode_config.max_height = 8192;
 dev->mode_config.async_page_flip = true;

「注册哪些KMS的API接口函数」用户空间通过ioctl调用此类KMS API接口,实现KMS的信息检查及提交。该初始化同样由DRM device(DRM master)完成注册。例如RK3399的DRM驱动:


void rockchip_drm_mode_config_init(struct drm_device *dev)
{
......
 dev->mode_config.funcs = &rockchip_drm_mode_config_funcs;
}

KMS funcs包括:

struct drm_mode_config_funcs {
 struct drm_framebuffer *(*fb_create)(struct drm_device *dev,
          struct drm_file *file_priv,
          struct drm_mode_fb_cmd2 *mode_cmd);
 void (*output_poll_changed)(struct drm_device *dev);

 int (*atomic_check)(struct drm_device *dev,
       struct drm_atomic_state *a);
 int (*atomic_commit)(struct drm_device *dev,
        struct drm_atomic_state *a,
        bool async);
 struct drm_atomic_state *(*atomic_state_alloc)(struct drm_device *dev);
 void (*atomic_state_clear)(struct drm_atomic_state *state);
 void (*atomic_state_free)(struct drm_atomic_state *state);
};

其中,最主要的是fb_createatomic_checkatomic_commit,例如RK3399注册了以上3个回调函数,另外注册了1个output_poll_changed回调函数。

static const struct drm_mode_config_funcs rockchip_drm_mode_config_funcs = {
 .fb_create = rockchip_user_fb_create,
 .output_poll_changed = rockchip_drm_output_poll_changed,
 .atomic_check = drm_atomic_helper_check,
 .atomic_commit = rockchip_drm_atomic_commit,
};

关于drm_mode_config_funcs各成员,我会在DRM KMS流程的文章中详细介绍。

以上便是此文的所有内容,介绍了KMS的前世今生以及实现机制,作为底层驱动开发者而言,KMS的注册机制至关重要,而对于上层应用开发者而言,了解DRM KMS能够支持哪些功能亦不可少。

END

转载请注明:XAMPP中文组官网 » Linux操作系统更改显示屏分辨率底层驱动实现原理 | 基于kernel DRM KMS

您必须 登录 才能发表评论!