Rockchip RK3399 - DRM子系统

从开始接触音频子系统到如今已经两个多月,说实话花费的时间的确有点长了。从今天起我们开始接触DRM,网上已经有很多优秀的关于DRM的文章了,因此我们学习直接去学习一些优秀的文章即可。后面有关DRM相关的文章我们会大量参考[1] DRM (Direct Rendering Manager)

一、DRM介绍

1.1 DRM概述

linux内核中包含两类图形显示设备驱动框架:

  • FB设备:Frame buffer图形显示框架;
  • DRM:直接渲染管理器(Direct Rendering Manager),是linux目前主流的图形显示框架;

在实际场景中,具体选择哪一种图像设备驱动框架取决于我们自己的业务需求。

1.1.1 Frambebuffer驱动

Frambebuffer驱动具有以下特征:

  • 直接控制显卡的帧缓冲区,提供基本的显卡输出功能;

  • 使用一些内核数据结构和API来管理图形界面,并提供一组接口与用户空间的应用程序进行通信;

  • 相对简单,适合于嵌入式系统或者不需要高性能图形的应用场景。

1.1.2 DRM驱动

相比FB(frame buffer)架构,DRM更能适应当前日益更新的显示硬件;

  • 提供一种分离的图形驱动架构,将硬件驱动程序、内核模块和用户空间驱动程序进行分离;
  • 支持多个应用程序同时访问显卡,并提供了更丰富的图形功能,例如硬件加速和3D加速;
  • 提供了一些内核接口,可以让用户空间应用程序与驱动程序进行交互;
  • 支持多显示器(Display)和多GPU的配置;

总之,一句话,DRMLinux目前主流的图形显示框架,相比FB架构,DRM更能适应当前日益更新的显示硬。尽管FB退出历史舞台,在DRM中也并未将其遗弃,而是集合到DRM中,供部分嵌入式设备使用。

有关DRM的发展历史可以参考这篇博客:DRM (Direct Rendering Manager) 的发展历史

img

1.2 DRM框架

我们来看一下DRM子系统的软件架构:

img

DRM框架从上到下依次为应用程序、libdrmDRM driverVideo Card

(1) 应用程序:上图中并没有画出;

(2) libdrmlbdrmDRM框架提供的位于用户空间操作DRM的库,提供了DRM驱动的用户空间接口;对底层接口进行封装,向上层应用程序提供通用的API接口,本质上是对各种IOCTL接口进行封装;

(3) DRM coreDRM核心层,由GEMKMS组成;

  • KMSKernel Mode Setting,所谓内核显示模式设置,其实说白了就两件事:更新画面和设置显示参数;
    • 更新画面:显示buffer的切换,多图层的合成方式,以及每个图层的显示位置;
    • 设置显示参数:包括分辨率、刷新率、电源状态(休眠唤醒)等;
  • GEMGraphic Execution Manager(图形执行管理器),它提供了一种抽象的显存管理方式,使用户空间应用程序可以更方便地管理显存,而不需要了解底层硬件的细节;

(4) Video CardGPU以及显卡驱动;

1.2.1 KMS

KMS主要负责显示相关功能,在DRM中将其进行抽象,包括:CRTCENCODERCONNECTORPLANEFramebufferVBLANKproperty;它们之间的关系如下图所示:

HDMI接口为例说明,Soc内部一般包含一个Display模块,通过总线连接到HDMI接口上;

  • Display模块对应CRTC
  • HDMI接口对应Connector
  • Framebuffer对应的是显存部分;
  • Plane是对Framebuffer进行描述的部分;
  • Encoder是将像素转化为HDMI接口所需要的信号,一般EncoderConnector放到一块初始化。
1.2.2 GEM

GEM主要负责显示buffer的分配和释放,在DRM中将其进行抽象,包括:DUMPPRIMEfence

1.2.3 元素介绍

学习DRM驱动其实就是学习上面各个元素的实现及用法,如果你能掌握这些知识点,那么在编写DRM驱动的时候就能游刃有余。

元素 说明
CRTC Framebuffer中读取待显示的图像,并按照响应的格式输出给encoder,其主要承担的作用为
(1)配置适合显示的显示模式、分辨率、刷新率等参数,并输出相应的时序;
(2)扫描Framebuffer发送到一个或多个显示器;
(3)更新Framebuffer
概括下就是,对显示器进行扫描,产生时序信号的模块、负责帧切换、电源控制、色彩调整等等。
NCODER 负责将CRTC输出的timing时序转换成外部设备所需要的信号的模块,如HDMI转换器或DSI Controller
CONNECTOR 连接物理显示设备的连接器,如HDMIDisplayPortDSI总线,通常和Encoder驱动绑定在一起
PLANE 图层,实际输出的图像是多个图层叠加而成的,比如主图层、光标图层。其中有些图层由硬件加速模块生成,每个CRTC至少一个planeplane一共有三种,分别是:DRM_PLANE_TYPE_PRIMARYDRM_PLANE_TYPE_OVERLAYDRM_PLANE_TYPE_CURSOR。这是配置plane的三个枚举,标注主图层、覆盖图层、光标图层;
FB Framebuffer,用于存储单个图层要实现的内容
VBLANK 软件和硬件的同步机制,RGB时序中的垂直消影区,软件通常使用硬件VSYNC来实现
property 任何你想设置的参数都可以做成property,是DRM驱动中最灵活、最方便的Mode setting机制
DUMB 只支持连续物理内存,基于kernel中通用CMA API实现,多用于小分辨率简单场景
PRIME 连续、非连续物理内存都支持,基于DMA-BUF机制,可以实现buffer共享,多用于大内存复杂场景
fence buffer同步机制,基于内核dma_fence机制实现,用于防止显示内容出现异步问题
1.3 目录结构

linux内核将DRM驱动相关的代码都放在drivers/gpu/drm目录下,这下面的文件还是比较多的,我们大概了解一下即可;

root@zhengyang:/work/sambashare/rk3399/linux-6.3# ls drivers/gpu/drm/ -I "*.o"
amd                         drm_fbdev_generic.c             drm_print.c                logicvc
arm                         drm_fb_dma_helper.c             drm_privacy_screen.c       Makefile
armada                      drm_fb_helper.c                 drm_privacy_screen_x86.c   mcde
aspeed                      drm_file.c                      drm_probe_helper.c         mediatek
ast                         drm_flip_work.c                 drm_property.c             meson
atmel-hlcdc                 drm_format_helper.c             drm_rect.c                 mgag200
bridge                      drm_fourcc.c                    drm_scatter.c              modules.order
built-in.a                  drm_framebuffer.c               drm_self_refresh_helper.c  msm
display                     drm_gem_atomic_helper.c         drm_shmem_helper.ko        mxsfb
drm_agpsupport.c            drm_gem.c                       drm_shmem_helper.mod       nouveau
drm_aperture.c              drm_gem_dma_helper.c            drm_shmem_helper.mod.c     omapdrm
drm_atomic.c                drm_gem_framebuffer_helper.c    drm_simple_kms_helper.c    panel
drm_atomic_helper.c         drm_gem_shmem_helper.c          drm_syncobj.c              panfrost
drm_atomic_state_helper.c   drm_gem_ttm_helper.c            drm_sysfs.c                pl111
drm_atomic_uapi.c           drm_gem_vram_helper.c           drm_trace.h                qxl
drm_auth.c                  drm_hashtab.c                   drm_trace_points.c         radeon
drm_blend.c                 drm_internal.h                  drm_ttm_helper.ko          rcar-du
drm_bridge.c                drm_ioc32.c                     drm_ttm_helper.mod         rockchip
drm_bridge_connector.c      drm_ioctl.c                     drm_ttm_helper.mod.c       scheduler
drm_buddy.c                 drm_irq.c                       drm_vblank.c               shmobile
drm_bufs.c                  drm_kms_helper_common.c         drm_vblank_work.c          solomon
drm_cache.c                 drm_lease.c                     drm_vma_manager.c          sprd
drm_client.c                drm_legacy.h                    drm_vm.c                   sti
drm_client_modeset.c        drm_legacy_misc.c               drm_vram_helper.ko         stm
drm_color_mgmt.c            drm_lock.c                      drm_vram_helper.mod        sun4i
drm_connector.c             drm_managed.c                   drm_vram_helper.mod.c      tegra
drm_context.c               drm_memory.c                    drm_writeback.c            tests
drm_crtc.c                  drm_mipi_dbi.c                  etnaviv                    tidss
drm_crtc_helper.c           drm_mipi_dsi.c                  exynos                     tilcdc
drm_crtc_helper_internal.h  drm_mm.c                        fsl-dcu                    tiny
drm_crtc_internal.h         drm_mode_config.c               gma500                     ttm
drm_damage_helper.c         drm_mode_object.c               gud                        tve200
drm_debugfs.c               drm_modes.c                     hisilicon                  udl
drm_debugfs_crc.c           drm_modeset_helper.c            hyperv                     v3d
drm_displayid.c             drm_modeset_lock.c              i2c                        vboxvideo
drm_dma.c                   drm_of.c                        i915                       vc4
drm_drv.c                   drm_panel.c                     imx                        vgem
drm_dumb_buffers.c          drm_panel_orientation_quirks.c  ingenic                    virtio
drm_edid.c                  drm_pci.c                       Kconfig                    vkms
drm_edid_load.c             drm_plane.c                     kmb                        vmwgfx
drm_encoder.c               drm_plane_helper.c              lib                        xen
drm_encoder_slave.c         drm_prime.c                     lima                       xlnx

其中rockchipRockchip官方的实现代码:

root@zhengyang:/work/sambashare/rk3399/linux-6.3# ls drivers/gpu/drm/rockchip/ -I "*.o"
analogix_dp-rockchip.c  inno_hdmi.c         rockchip_drm_drv.h   rockchip_drm_vop.h
built-in.a              inno_hdmi.h         rockchip_drm_fb.c    rockchip_lvds.c
cdn-dp-core.c           Kconfig             rockchip_drm_fb.h    rockchip_lvds.h
cdn-dp-core.h           Makefile            rockchip_drm_gem.c   rockchip_rgb.c
cdn-dp-reg.c            modules.order       rockchip_drm_gem.h   rockchip_rgb.h
cdn-dp-reg.h            rk3066_hdmi.c       rockchip_drm_vop2.c  rockchip_vop2_reg.c
dw_hdmi-rockchip.c      rk3066_hdmi.h       rockchip_drm_vop2.h  rockchip_vop_reg.c
dw-mipi-dsi-rockchip.c  rockchip_drm_drv.c  rockchip_drm_vop.c   rockchip_vop_reg.h

二、硬件抽象

对于初学者来说,往往让人迷惑的不是DRMobjects的概念,而是如何去建立这些objects与实际硬件的对应关系。因为并不是所有的Display硬件都能很好的对应上plane/crtc/encoder/connector这些objects

在学如何去抽象显示硬件到具体的RM object之前,我们先普及一下MIPI相关的知识。

MIPI(Mobile Industry Processor Interface)是2003年由ARM, Nokia, ST ,TI等公司成立的一个联盟,目的是把手机内部的接口如摄像头、显示屏接口、射频/基带接口等标准化,从而减少手机设计的复杂程度和增加设计灵活性。

MIPI联盟下面有不同的WorkGroup,分别定义了一系列的手机内部接口标准,比如:

  • 摄像头接口CSI(Camera Serial Interface)
  • 显示接口DSI(Display Serial Interface)
  • 射频接口DigRF
  • 麦克风/喇叭接口SLIMbus等。
2.1 MIPI DSI 接口

下图为一个典型的MIPI DSI接口屏的硬件连接框图:

img

它在软件架构上与DRM object的对应关系如下图:

img

多余的细节不做介绍,这里只说明为何如此分配drm object

object 说明
crtc RGB timing的产生,以及显示数据的更新,都需要访问Dislay Controller硬件寄存器,因此放在Display Controller驱动中
plane Overlay硬件的抽象,同样需要访问Display Controller寄存器,因此也放在Display Controller驱动中
encoder RGB并行信号转换为DSI行信号,需要配置DSI硬件寄存器,因此放在DSI Controller驱动中
connector 可以通过drm_panel来获取LCDmode信息,但是encoder在哪,connector就在哪,因此放在DSI Controller驱动中
drm_panel 用于获取LCD mode参数,并提供LCD休眠唤醒的回调接口,供encoder调用,因此放在LCD驱动中

驱动参考:https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/panel/panel-ilitek-ili9881c.c。

有关MIPI DSI可以参考MIPI 系列之 DSI

2.2 MIPI DPI接口

DPI接口也就是我们常说的RGB并行接口,Video数据通过RGB并行总线传输,控制命令(如初始化、休眠、唤醒等)则通过SPI/I2C 总线传输,比如早期的S3C2440 SoC平台。下图为一个典型的MIPI DPI接口屏的硬件连接框图:

在这里插入图片描述

该硬件连接在软件架构上与DRM object的对应关系如下图:

在这里插入图片描述

多余的细节不做介绍,这里只说明为何如此分配drm object

object 说明
crtc RGB timing的产生,以及显示数据的更新,都需要访问LCD Controller硬件寄存器,因此放在LCD Controller驱动中
plane LCDC没有Overlay硬件,它只有一个数据源通道,被抽象为Primary Plane,同样需要访问 LCDC硬件寄存器,因此放在LCDC驱动中
encoder 由于DPI接口本身不需要对RGB信号做任何转换,因此没有哪个硬件与之对应。但是drm objects又缺一不可,因此实现了一个虚拟的encoder object。至于为什么要放在LCDC驱动中实现,纯粹只是为了省事而已,你也可以放在一个虚拟的平台驱动中去实现该encoder object
connector encoder在哪,connector就在哪,没什么好说的了
drm_panel 用于获取LCD mode参数,并提供LCD休眠唤醒的回调接口,供encoder调用,因此放在LCD驱动中

驱动参考:https://elixir.bootlin.com/linux/v5.0/source/drivers/gpu/drm/panel/panel-sitronix-st7789v.c。

2.3 MIPI DBI接口

DBI接口也就是我们平时常说的MCUSPI接口屏,这类屏的VIDEO数据和控制命令都是通过同一总线接口(I80、SPI接口)进行传输,而且这类屏幕必须内置GRAM显存,否则屏幕无法维持正常显示。

下图为一个典型的DBI接口屏的硬件连接框图:

在这里插入图片描述

该硬件连接在软件架构上与DRM object的对应关系如下:

在这里插入图片描述

上图参考kernel4.19 tinydrm软件架构。

object 说明
crtc 这类硬件本身不需要任何RGB timing信号,因此也没有实际的硬件与之对应。但是drm objects缺一不可,需要实现一个虚拟的crtc object。由于更新图像数据的动作需要通过SPI总线发送命令才能完成,因此放在了LCD驱动中
plane 没有实际的硬件与之对应,但crtc初始化时需要一个plane object作为参数传递,因此和crtc放在一起
encoder 没有实际的硬件与之对应,使用虚拟的encoder object。因为这类硬件并不是将RGB信号转换为SPI信号,而是根本就没有RGB信号源,也就无从谈起encoder设备。但是为了通知LCD休眠唤醒,需要调用LCD驱动的相应接口,因此放在LCD驱动中
connector 由于没有了drm_panel,需要调用LCD接口来获取mode参数,因此放在LCD驱动中

驱动参考:https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/tinydrm/ili9341.c。

三、DRM Objects

在编写DRM驱动程序之前,我们先对DRM内部的Objects进行一番介绍,因为这些ObjectsDRM框架的核心,它们缺一不可。

img

上图蓝色部分则是对物理硬件的抽象,黄色部分则是对软件的抽象。虚线以上的为drm_mode_object(或者说是`modset object

这些objects之间的关系:

img

通过上图可以看到,plane是连接framebuffercrtc的纽带,而encoder则是连接crtcconnector的纽带。与物理buffer直接打交道的是gem而不是framebuffer

需要注意的是,上图蓝色部分即使没有实际的硬件与之对应,在软件驱动中也需要实现这些objects,否则DRM子系统无法正常运行。

3.1 modeset object

对于planecrtcencoderconnector几个对象,它们有一个公共基类struct drm_mode_object,这几个对象都由此基类扩展而来(该类作为crtc等结构体的成员)。事实上这个基类扩展出来的子类并不是只有上面提到的几种,只不过这四种比较常见。其定义在include/drm/drm_mode_object.h

/**
 * struct drm_mode_object - base structure for modeset objects
 * @id: userspace visible identifier
 * @type: type of the object, one of DRM_MODE_OBJECT\_\*
 * @properties: properties attached to this object, including values
 * @refcount: reference count for objects which with dynamic lifetime
 * @free_cb: free function callback, only set for objects with dynamic lifetime
 *
 * Base structure for modeset objects visible to userspace. Objects can be
 * looked up using drm_mode_object_find(). Besides basic uapi interface
 * properties like @id and @type it provides two services:
 *
 * - It tracks attached properties and their values. This is used by &drm_crtc,
 *   &drm_plane and &drm_connector. Properties are attached by calling
 *   drm_object_attach_property() before the object is visible to userspace.
 *
 * - For objects with dynamic lifetimes (as indicated by a non-NULL @free_cb) it
 *   provides reference counting through drm_mode_object_get() and
 *   drm_mode_object_put(). This is used by &drm_framebuffer, &drm_connector
 *   and &drm_property_blob. These objects provide specialized reference
 *   counting wrappers.
 */
struct drm_mode_object {
        uint32_t id;
        uint32_t type;
        struct drm_object_properties *properties;
        struct kref refcount;
        void (*free_cb)(struct kref *kref);
};

包括以下成员:

  • id:用户空间可见的唯一标识标识符,基于idr算法分配得到的;

  • type:对象的类型,可以是DRM_MODE_OBJECT_*中的一个;

  • properties:附加到该对象的属性,包括属性的值;在DRM驱动中,每个对象都可以拥有一组属性(例如分辨率、刷新率等),并且可以动态地增加、删除或修改属性。这些属性可以被用户空间的应用程序或者其他驱动程序获取或者设置;

  • refcount:具有动态生命周期的对象的引用计数;指drm_mode_object对象在内核中的生命周期的管理,每个drm_mode_object对象都有一个引用计数;

    • 当一个对象被创建时,它的引用计数被初始化为1;
    • 每当一个新的引用指向该对象时,它的引用计数就会增加1;
    • 每当一个引用被释放时,它的引用计数就会减少1;
    • 当对象的引用计数降为0时,内核会自动释放该对象。
    • 这种方式确保了内核中不会存在不再使用的对象,从而避免了内存泄漏。
  • free_cb:释放函数回调,仅对具有动态生命周期的对象设置;

为了更加清晰的了解struct drm_mode_objectstruct drm_object_propertiesstruct snd_jack_kctl数据结构的关系,我们绘制了如下关系图:

3.2 对象类型

type主要包含以下几种类型,定义在include/uapi/drm/drm_mode.h

#define DRM_MODE_OBJECT_CRTC 0xcccccccc
#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
#define DRM_MODE_OBJECT_ENCODER 0xe0e0e0e0
#define DRM_MODE_OBJECT_MODE 0xdededede
#define DRM_MODE_OBJECT_PROPERTY 0xb0b0b0b0
#define DRM_MODE_OBJECT_FB 0xfbfbfbfb
#define DRM_MODE_OBJECT_BLOB 0xbbbbbbbb
#define DRM_MODE_OBJECT_PLANE 0xeeeeeeee
#define DRM_MODE_OBJECT_ANY 0

该结构体提供了用户空间可见的modeset objects的基本结构,可以通过drm_mode_object_find函数查找对象。

3.3 对象属性

struct drm_object_properties用于描述对象的属性,定义在include/drm/drm_mode_object.h

/**
 * struct drm_object_properties - property tracking for &drm_mode_object
 */
struct drm_object_properties {
        /**
         * @count: number of valid properties, must be less than or equal to
         * DRM_OBJECT_MAX_PROPERTY.
         */

        int count;
        /**
         * @properties: Array of pointers to &drm_property.
         *
         * NOTE: if we ever start dynamically destroying properties (ie.
         * not at drm_mode_config_cleanup() time), then we'd have to do
         * a better job of detaching property from mode objects to avoid
         * dangling property pointers:
         */
        struct drm_property *properties[DRM_OBJECT_MAX_PROPERTY];

        /**
         * @values: Array to store the property values, matching @properties. Do
         * not read/write values directly, but use
         * drm_object_property_get_value() and drm_object_property_set_value().
         *
         * Note that atomic drivers do not store mutable properties in this
         * array, but only the decoded values in the corresponding state
         * structure. The decoding is done using the &drm_crtc.atomic_get_property and
         * &drm_crtc.atomic_set_property hooks for &struct drm_crtc. For
         * &struct drm_plane the hooks are &drm_plane_funcs.atomic_get_property and
         * &drm_plane_funcs.atomic_set_property. And for &struct drm_connector
         * the hooks are &drm_connector_funcs.atomic_get_property and
         * &drm_connector_funcs.atomic_set_property .
         *
         * Hence atomic drivers should not use drm_object_property_set_value()
         * and drm_object_property_get_value() on mutable objects, i.e. those
         * without the DRM_MODE_PROP_IMMUTABLE flag set.
         *
         * For atomic drivers the default value of properties is stored in this
         * array, so drm_object_property_get_default_value can be used to
         * retrieve it.
         */
        uint64_t values[DRM_OBJECT_MAX_PROPERTY];
};

该结构体包含以下字段:

  • countproperties数组长度,必须小于或等于DRM_OBJECT_MAX_PR
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Graceful_scenery

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值