LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

Linux显示(三):DRM子系统(以及LCDC/Panel/Backlight驱动)

 关键词:DRM、CRTC、Plane、Framebuffer、Encoder、Connector、Bridge、Panel、Backlight、GEM、TTM、KMS等等。

DRM(Direct Rendering Manager)是Linux内核中负责与显卡交互的管理架构,用户空间很方便的利用DRM提供的API,实现3D渲染、视频解码和GPU计算等工作。

基本概念说明

DRM(Direct Rendering Manager)

 DRM作为Linux负责显示的子系统,负责和显示控制器/GPU交互,对用户提供显示、3D渲染等操作。

Linux GPU Driver Developer’s Guide》提供了对Linux kernel DRM描述,包括内存管理TTM/GEM、KMS、各种抽象组件、DRM fbdev、libdrm等等。

TTM(Transplation Table Manager)

参考《DRM Memory Management — TTM》。
GEM(Graphics Execution Manager)

DRM下buffer管理和分配,类似ION、DMA BUFFER。

参考《DRM Memory Management — GEM》。

KMS(Kernel Mode Setting)  Kernel Mode Setting (KMS) — The Linux Kernel documentation

Framebuffer

Framebuffer是CRTC输出的像素源的抽象内存。

参考《Kernel Mode Setting (KMS) — Framebuffer Abstraction》,包括Framebuffer说明、Framebuffer数据结构、Framebuffer相关API。

Mode Setting Helper Functions — Framebuffer GEM Helper Reference》使用GEM给Framebuffer分配内存。

Mode Setting Helper Functions — Framebuffer CMA Helper Functions Reference》使用CMA给Framebuffer分配内存。

Plane

图层,SoC内部VOP模块win图层的抽象。

参考《Kernel Mode Setting (KMS) — Plane Abstraction》,包括Plane说明、数据结构、API。

CRTC(Cathode Ray Tube Controller)

CRTC(阴极射线管控制器)即显示控制器,内部VOP模块或者VOP2中Video Port的抽象

参考《Kernel Mode Setting (KMS) — CRTC Abstraction》,包括CRTC功能简单说明、CRTC相关数据结构、CRTC相关API说明。

Encoder

输出显示数据编码器,指的是RGB、LVDS、DSI、eDP、DP、HDMI、CVBS、VGA等格式编码。

参考《Kernel Mode Setting (KMS) — Encoder Abstraction》,包括Enncoder说明、数据结构、API。

Connector

连接器,指的是encoder和panel之间交互的接口部分。比如RGB、DSI、HDMI等引脚。

参考《Kernel Mode Setting (KMS) — Connector Abstraction》,包括Connector的说明、数据结构、API。

Bridge

桥接设备,一般用于注册Encoder后面另外再转接芯片,如DSI2HDMI转换芯片。

参考《Mode Setting Helper Functions — Bridge》,包括Bridge的说明、数据结构、API。

Panel

泛指屏幕,各种LCD显示设备的抽象。

参考《Mode Setting Helper Functions — Panel Helper Reference》,包括Panel说明、数据结构、API。

Mode Setting Helper Functions — Panel Self Refresh Helper Reference》支持Panel自刷新功能的API。

Backlight 作为Panel背光设备。
Vertical Blanking

 Vertical Blanking是帧同步功能,避免出现泪痕效应。

参考《Kernel Mode Setting (KMS) — Vertical Blanking》,包括Vblank的说明、数据结构、API。

fbdev

DRM下fbdev是基于DRM实现fb设备,用户空间可以通过访问Framebuffer子系统到DRM子系统对显示控制器进行操作。

参考《Mode Setting Helper Functions — fbdev Helper Functions Reference》,包括fbdev说明、数据结构、API。

Kernel clients

运行在Kernel内部的DRM client,比如fbdev。

参考《Kernel clients — Kernel clients》,包括Kernel clients的说明、数据结构、API等。

libdrm

DRM设备通过ioctl导出一系列命令到用户空间。libdrm对DRM设备导出到用空间的接口进行封装,这些接口包括:memory mapping, context management, DMA operations, AGP management, vblank control, fence management, memory management, and output management。

参考《Userland interfaces》。

 可以将Linux下DRM系统分为3部分:

  • DRM相关驱动,可以是LCDC驱动、Panel驱动、GPU驱动。
  • DRM Core,负责给底层驱动提供功能,对用户空间导出各种接口。
  • libdrm等上层应用,负责GUI框架、3D调用、调用底层显示控制和GPU功能等等。

 

1 DRM/LCD配置

 DRM相关的设置有:DRM使能、显示控制器驱动、背光驱动、Framebuffer驱动等。

Device Drivers
  Graphics support
    ->Direct Rendering Manager (XFree86
4.1.0 and higher DRI support)
      ->Insert extra checks and debug info into the DRM range managers
      ->Enable legacy fbdev support for your modesetting driver--支持基于DRM的模拟fbdev设备。
        ->Overallocation of the fbdev buffer
    ->DRM Support for STMicroelectronics SoC Series--STM32 LCDC驱动。       ->STMicroelectronics specific extensions for Synopsys MIPI DSI     ->Display Panels
      ->support for simple panels--包含一个regulator和gpio上电,以及一个backlight的DRM panel模板驱动。
    ->Display Interface Bridges --->    ->Frame buffer Devices--Framebuffer驱动。
      ->Support for frame buffer devices
        ->ARM PrimeCell PL110 support     ->Backlight & LCD device support
      ->Lowlevel Backlight controls--背光驱动。
        ->Generic PWM based Backlight Driver     ->Console display driver support--基于Framebuffer的console。

1.1 DRM子系统初始化

drm模块进行DRM子系统初始化:

  • 创建drm类。
  • 创建version节点。
  • 创建dri debugfs。
  • 注册drm字符设备。
  • 创建debug、edid_fixup、timestamp_precision_usec、vblankoffdelay等参数。
drm_core_init
  ->drm_connector_ida_init
  ->drm_sysfs_init
    ->class_create--创建drm类。
    ->class_create_file--创建/sys/class/drm/version节点。
  ->debugfs_create_dir--创建/sys/kernel/debug/dri目录。
  ->register_chrdev--注册drm chrdev设备,文件操作函数集为drm_stub_fops。

drm_kms_helper模块初始化:

drm_kms_helper_init
  drm_fb_helper_modinit--宏定义满足则等待fbcon模块进行初始化。
  drm_dp_aux_dev_init--创建drm_dp_aux_dev类,并注册aux设备。

模块参数包括:dp_aux_i2c_speed_khz、dp_aux_i2c_transfer_size、drm_fbdev_overalloc、fbdev_emulation、poll。

2 DRM文件

drivers/gpu
├── drm
│   ├── bridge
│   │   ├── panel.c
│   │   ├── synopsys
│   │   │   ├── dw-mipi-dsi.c--Synopsys DW MIPI DSI host驱动。
│   ├── drm_agpsupport.c
│   ├── drm_atomic.c
│   ├── drm_atomic_helper.c
│   ├── drm_atomic_state_helper.c
│   ├── drm_atomic_uapi.c
│   ├── drm_auth.c
│   ├── drm_blend.c
│   ├── drm_bridge.c--bridge添加、删除、附着、去附着等操作。
│   ├── drm_bufs.c
│   ├── drm_cache.c
│   ├── drm_client.c
│   ├── drm_client_modeset.c
│   ├── drm_color_mgmt.c
│   ├── drm_connector.c
│   ├── drm_context.c
│   ├── drm_crtc.c--CRTC初始化、注册、模式设置等。
│   ├── drm_crtc_helper.c
│   ├── drm_damage_helper.c
│   ├── drm_debugfs.c--DRM debugfs初始化,以及创建connector、edid、crtc等调试节点。
│   ├── drm_debugfs_crc.c
│   ├── drm_dma.c
│   ├── drm_dp_aux_dev.c--DRM DP Aux设备创建和处理函数。
│   ├── drm_dp_cec.c
│   ├── drm_dp_dual_mode_helper.c
│   ├── drm_dp_helper.c
│   ├── drm_dp_mst_topology.c
│   ├── drm_drv.c
│   ├── drm_dsc.c
│   ├── drm_dumb_buffers.c
│   ├── drm_edid.c
│   ├── drm_edid_load.c
│   ├── drm_encoder.c--encoder初始化、去初始化、注册、注销等。
│   ├── drm_encoder_slave.c
│   ├── drm_fb_cma_helper.c--DRM Framebuffer使用CMA分配内存。
│   ├── drm_fb_helper.c--DRM Framebuffer创建、操作函数集、配置等。
│   ├── drm_file.c
│   ├── drm_flip_work.c
│   ├── drm_format_helper.c
│   ├── drm_fourcc.c
│   ├── drm_framebuffer.c--基于DRM的Framebuffer的初始化、释放、ioctl等操作。
│   ├── drm_gem.c
│   ├── drm_gem_cma_helper.c
│   ├── drm_gem_framebuffer_helper.c
│   ├── drm_gem_shmem_helper.c
│   ├── drm_gem_vram_helper.c
│   ├── drm_hashtab.c
│   ├── drm_hdcp.c
│   ├── drm_ioc32.c
│   ├── drm_ioctl.c--DRM设备用于空间ioctl命令对应实现。
│   ├── drm_irq.c
│   ├── drm_kms_helper_common.c--创建drm_kms_helper模块,处理fdcon模块请求和dp auxdev创建。
│   ├── drm_lease.c
│   ├── drm_legacy_misc.c
│   ├── drm_lock.c
│   ├── drm_memory.c
│   ├── drm_mipi_dbi.c
│   ├── drm_mipi_dsi.c
│   ├── drm_mm.c
│   ├── drm_mode_config.c
│   ├── drm_mode_object.c
│   ├── drm_modes.c
│   ├── drm_modeset_helper.c
│   ├── drm_modeset_lock.c
│   ├── drm_of.c--处理DRM的DTS属性。
│   ├── drm_panel.c--panel的初始化、添加、附着、使能以及对应反向操作等。
│   ├── drm_panel_orientation_quirks.c
│   ├── drm_pci.c
│   ├── drm_plane.c
│   ├── drm_plane_helper.c
│   ├── drm_prime.c
│   ├── drm_print.c
│   ├── drm_probe_helper.c--探测connector的帮助函数。
│   ├── drm_property.c
│   ├── drm_rect.c
│   ├── drm_scatter.c
│   ├── drm_scdc_helper.c
│   ├── drm_self_refresh_helper.c
│   ├── drm_simple_kms_helper.c
│   ├── drm_syncobj.c
│   ├── drm_sysfs.c
│   ├── drm_trace_points.c
│   ├── drm_vblank.c
│   ├── drm_vma_manager.c
│   ├── drm_vm.c
│   ├── drm_vram_helper_common.c
│   ├── drm_vram_mm_helper.c
│   ├── drm_writeback.c
│   ├── Makefile
│   ├── stm
│   │   ├── drv.c--STM32 LTDC platform_driver驱动。
│   │   ├── dw_mipi_dsi-stm.c--STM32 DSI platform_driver驱动。
│   │   ├── ltdc.c--STM32 LTDC底层驱动主题函数实现。
│   ├── panel
│   │   ├── panel-simple.c--Panel驱动程序。

3 DRM数据结构和API

3.1 DRM数据结构

struct drm_device是一个虚拟的设备,可以将DRM KMS各种组件关联到设备上。struct drm_driver是一类DRM设备的驱动程序。

struct drm_framebuffer表示一个提供给CRTC的抽象内存对象。

struct drm_framebuffer {
    struct drm_device *dev;
    struct list_head head;
    struct drm_mode_object base;
    char comm[TASK_COMM_LEN];
    const struct drm_format_info *format;
    const struct drm_framebuffer_funcs *funcs;
    unsigned int pitches[4];
    unsigned int offsets[4];
    uint64_t modifier;
    unsigned int width;
    unsigned int height;
    int flags;
    int hot_x;
    int hot_y;
    struct list_head filp_head;
    struct drm_gem_object *obj[4];
};

struct drm_plane表示一个输入到CRTC的图像源,上游是drm_framebuffer,下游是drm_crtc。

struct drm_plane {
    struct drm_device *dev;
    struct list_head head;
    char *name;
    struct drm_modeset_lock mutex;
    struct drm_mode_object base;
    uint32_t possible_crtcs;
    uint32_t *format_types;
    unsigned int format_count;
    bool format_default;
    uint64_t *modifiers;
    unsigned int modifier_count;
    struct drm_crtc *crtc;--plane的下游。
    struct drm_framebuffer *fb;--plane的上游。
    struct drm_framebuffer *old_fb;
    const struct drm_plane_funcs *funcs;
    struct drm_object_properties properties;
    enum drm_plane_type type;
    unsigned index;
    const struct drm_plane_helper_funcs *helper_private;
    struct drm_plane_state *state;
...
};

struct drm_crtc表示整个显示流,从一个或多个drm_plane接收像素数据,然后将其融合输出到一个或多个drm_encoder。

struct drm_crtc {
    struct drm_device *dev;
    struct device_node *port;
    struct list_head head;
    char *name;
    struct drm_modeset_lock mutex;
    struct drm_mode_object base;
    struct drm_plane *primary;
    struct drm_plane *cursor;
    unsigned index;
    int cursor_x;
    int cursor_y;
    bool enabled;
    struct drm_display_mode mode;
    struct drm_display_mode hwmode;
    int x;
    int y;
    const struct drm_crtc_funcs *funcs;
...
};
 

struct drm_encoder表示CRTC和Connector之间的连接单元,从drm_crtc获取像素数据,转换成适合附着其上的drm_bridge。

struct drm_encoder {
    struct drm_device *dev;
    struct list_head head;
    struct drm_mode_object base;
    char *name;
    int encoder_type;
    unsigned index;
    uint32_t possible_crtcs;
    uint32_t possible_clones;
    struct drm_crtc *crtc;--Encoder上游CRTC。
    struct drm_bridge *bridge;--Encoder下游。
    const struct drm_encoder_funcs *funcs;
    const struct drm_encoder_helper_funcs *helper_private;
};

struct drm_connector将Encoder输出的像素数据传递给Panel。

struct drm_connector {
    struct drm_device *dev;
    struct device *kdev;
    struct device_attribute *attr;
    struct list_head head;
    struct drm_mode_object base;
    char *name;
...
    struct drm_encoder *encoder;--Connector上游。
...
};

struct drm_bridge挂在Encoder后端,其后端可以是Connector或者Bridge。

struct drm_bridge {
    struct drm_device *dev;
    struct drm_encoder *encoder;--Bridge上游。
    struct drm_bridge *next;
#ifdef CONFIG_OF
    struct device_node *of_node;
#endif
    struct list_head list;
    const struct drm_bridge_timings *timings;
    const struct drm_bridge_funcs *funcs;
    void *driver_private;
};

struct drm_panel表示一个显示设备,作为DRM显示链路的末端,其上游是Connector。

struct drm_panel {
    struct drm_device *drm;
    struct drm_connector *connector;--Panel上游。
    struct device *dev;
    const struct drm_panel_funcs *funcs;
    struct list_head list;
};

struct drm_client_dev表示DRM client设备。

struct drm_client_dev {
    struct drm_device *dev;
    const char *name;
    struct list_head list;
    const struct drm_client_funcs *funcs;
    struct drm_file *file;
    struct mutex modeset_mutex;
    struct drm_mode_set *modesets;
};

常见的KMS mode-setting pipeline如下:

3.2 DRM API

3.2.1 DRM Device

DRM Internals》中介绍了drm_devicedrm_driver数据接口和API函数,以及drm_driver示例。

drm_dev_alloc()主要分配并初始化drm_device结构体:

drm_dev_alloc
  drm_dev_init
    drm_minor_alloc
      drm_sysfs_minor_alloc--分配/dev/dri/card0设备对应的结构体。
    drm_gem_init--分配并初始化DRM设备的GEM。

 drm_dev_register()是DRM Core核心:

  • 创建子设备的debugfs、sysfs等节点。
  • 创建/dev/dri/cardX设备。
  • 进行plane/crtc/encoder/connector的late_register,以及创建connector的sysfs和debugfs节点。
drm_dev_register
  ->drm_minor_register
    ->drm_debugfs_init
      ->debugfs_create_dir--创建/sys/kernel/debug/dri/0子目录。
      ->drm_debugfs_list--创建类似/sys/kernel/debug/dri/0/name、clients、gem_names子节点。
      ->drm_framebuffer_debugfs_init--创建/sys/kernel/debug/dri/0/framebuffer,输出framebuffer信息。
      ->drm_client_debugfs_init--创建类似/sys/kernel/debug/dri/0/internal_clients,输出DRM设备的客户列表。
    ->device_add--创建/dev/dri/card0设备。
  ->create_compat_control_link
  ->drm_modeset_register_all
    ->drm_plane_register_all--遍历plane的late_register()函数。
    ->drm_crtc_register_all--遍历crtc的late_register()函数。
    ->drm_encoder_register_all--遍历encoder的late_register()函数。
    ->drm_connector_register_all
      ->drm_connector_register--创建connector的sysfs、debugfs节点,并调用late_register()函数。
        ->drm_sysfs_connector_add
          ->device_create_with_groups--在设备下创建drm/card0/card0-DPI-1类似目录,sysfs节点为status、enabled、dpms、modes、edid等。
          ->drm_sysfs_hotplug_event--发送一个HOTPLUG=1的uevent。
        ->drm_debugfs_connector_add--创建类似/sys/kernel/debug/dri/0/DPI-1目录,节点为force、edid_override。
        ->drm_mode_object_register

以CMA分配方式为例,用户空间对DRM设备的read/write/ioctl都会落到如下函数集中:

#define DEFINE_DRM_GEM_CMA_FOPS(name) \
    static const struct file_operations name = {\
        .owner        = THIS_MODULE,\
        .open        = drm_open,\
        .release    = drm_release,\
        .unlocked_ioctl    = drm_ioctl,\
        .compat_ioctl    = drm_compat_ioctl,\
        .poll        = drm_poll,\
        .read        = drm_read,\
        .llseek        = noop_llseek,\
        .mmap        = drm_gem_cma_mmap,\
        DRM_GEM_CMA_UNMAPPED_AREA_FOPS \
    }

#define DRM_GEM_CMA_UNMAPPED_AREA_FOPS \
    .get_unmapped_area    = drm_gem_cma_get_unmapped_area,

特别关注一下drm_ioctl():

drm_ioctl
  ->[DRM_COMMAND_BASE, DRM_COMMAND_END)是特定驱动的ioctl命令。
  ->小于DRM_CORE_IOCTL_COUNT是DRM core固定的ioctl命令drm_ioctls。
  ->drm_ioctl_kernel
    ->drm_ioctl_permit--对调用者进行权限检查。
    ->func()--执行ioctl命令对应的函数。

drm_ioctls[]定义了丰富的命令列表:

/* Ioctl table */
static const struct drm_ioctl_desc drm_ioctls[] = {
    DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_RENDER_ALLOW),
    DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
    DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
    DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
...
    DRM_IOCTL_DEF(DRM_IOCTL_CRTC_GET_SEQUENCE, drm_crtc_get_sequence_ioctl, 0),
    DRM_IOCTL_DEF(DRM_IOCTL_CRTC_QUEUE_SEQUENCE, drm_crtc_queue_sequence_ioctl, 0),
    DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_LEASE, drm_mode_create_lease_ioctl, DRM_MASTER),
    DRM_IOCTL_DEF(DRM_IOCTL_MODE_LIST_LESSEES, drm_mode_list_lessees_ioctl, DRM_MASTER),
    DRM_IOCTL_DEF(DRM_IOCTL_MODE_GET_LEASE, drm_mode_get_lease_ioctl, DRM_MASTER),
    DRM_IOCTL_DEF(DRM_IOCTL_MODE_REVOKE_LEASE, drm_mode_revoke_lease_ioctl, DRM_MASTER),
};

3.2.2 DRM Framebuffer

如果打开CONFIG_DRM_FBDEV_EMULATION,则支持在DRM基础上创建fddev设备,注册到Framebuffer子系统。

DRM中fbdev作为DRM client,创建drm_fb_helper表示fbdev。

struct drm_fb_helper {
    struct drm_client_dev client;--drm fb对应的DRM client。
    struct drm_client_buffer *buffer;
    struct drm_framebuffer *fb;
    struct drm_device *dev;--drm fb所属的drm设备。
    const struct drm_fb_helper_funcs *funcs;
    struct fb_info *fbdev;--模拟的fbdev设备信息。
    u32 pseudo_palette[17];
    struct drm_clip_rect dirty_clip;
    spinlock_t dirty_lock;
    struct work_struct dirty_work;
    struct work_struct resume_work;
    struct mutex lock;
    struct list_head kernel_fb_list;
    bool delayed_hotplug;
    bool deferred_setup;
    int preferred_bpp;
};

drm_fbdev_generic_setup()在DRM上层创建一个模拟的fbdev作为DRM client。

  • 创建drm_fb_helper表示fbdev作为DRM Client。
  • 根据drm_client_dev特性选择合适的crtc、connector。
  • 为fbdev创建framebuffer内存。
  • 注册drm_client_dev。
drm_fbdev_generic_setup
  ->drm_client_init--初始化名称为fbdev的drm_fb_helper作为DRM client,操作函数集为drm_fbdev_client_funcs。
  ->drm_fbdev_client_hotplug
    ->drm_fb_helper_prepare--初始化drm_fb_helper,以及配置处理函数drm_fb_helper_generic_probe()。
    ->drm_fb_helper_init
    ->drm_fb_helper_initial_config
      ->__drm_fb_helper_initial_config_and_unlock
        ->drm_client_modeset_probe--为drm_client_dev选择合适的crtc、connector。
          ->drm_client_modeset_probe--根据crtc的最大width/height来对drm_client_device进行modeset。
            ->drm_client_connectors_enabled--使能connectors。
            ->drm_client_firmware_config--是否使用固件提供的配置。
            ->drm_client_target_preferred
              ->drm_connector_has_preferred_mode--根据crtc的最大width/height从connector中查找优选显示模式。
            ->drm_client_pick_crtcs--根据connector和width/height选择合适的crtc。
        ->drm_fb_helper_single_fb_probe
          ->drm_fb_helper_generic_probe--
            ->drm_client_framebuffer_create--创建一个drm_client_buffer类型的framebuffer,及其使用的内存。
            ->drm_fb_helper_alloc_fbi--分配fb_info。
            ->drm_fb_helper_fill_info--初始化并填充fb_info,对应的操作函数集为drm_fbdev_fb_ops。
        ->drm_setup_crtcs_fb
        ->register_framebuffer--根据drm_fb_helper_generic_probe创建的fb_info,注册一个FrameBuffer设备/dev/fb0。操作函数集为drm_fbdev_fb_ops。
  drm_client_register--将drm_client_dev注册到DRM子系统,即加入到当前drm_device的clientlist上。

Framebuffer子系统对接DRM fbdev主要通过drm_fbdev_fb_ops进行:

#define DRM_FB_HELPER_DEFAULT_OPS \
    .fb_check_var    = drm_fb_helper_check_var, \
    .fb_set_par    = drm_fb_helper_set_par, \
    .fb_setcmap    = drm_fb_helper_setcmap, \
    .fb_blank    = drm_fb_helper_blank, \
    .fb_pan_display    = drm_fb_helper_pan_display, \
    .fb_debug_enter = drm_fb_helper_debug_enter, \
    .fb_debug_leave = drm_fb_helper_debug_leave, \
    .fb_ioctl    = drm_fb_helper_ioctl

static struct fb_ops drm_fbdev_fb_ops = {
    .owner        = THIS_MODULE,
    DRM_FB_HELPER_DEFAULT_OPS,
    .fb_open    = drm_fbdev_fb_open,
    .fb_release    = drm_fbdev_fb_release,
    .fb_destroy    = drm_fbdev_fb_destroy,
    .fb_mmap    = drm_fbdev_fb_mmap,
    .fb_read    = drm_fb_helper_sys_read,
    .fb_write    = drm_fb_helper_sys_write,
    .fb_fillrect    = drm_fb_helper_sys_fillrect,
    .fb_copyarea    = drm_fb_helper_sys_copyarea,
    .fb_imageblit    = drm_fb_helper_sys_imageblit,
};

上述函数被Framebuffer自此同的fb_fops成员函数调用。 

4 LCDC驱动(STM32 LTDC)

4.1 LTDC dts

LTDC(LCD-TFT  Display Controller)是STM32 RGB并口显示控制器。其DTS包括寄存器、时钟、中断、pinctrl、复位,以及两个表示Connector的endpoint。

ltdc: display-controller@5a001000 {
    compatible = "st,stm32-ltdc";
    reg = <0x5a001000 0x400>;
    interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>,
             <GIC_SPI 89 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&rcc LTDC_PX>;
    clock-names = "lcd";
    resets = <&rcc LTDC_R>;
    status = "disabled";
};

&ltdc {
 pinctrl-names = "default", "sleep";
 pinctrl-0 = <&ltdc_pins_b>;
 pinctrl-1 = <&ltdc_pins_sleep_b>;
 status = "okay";

 port {
  #address-cells = <1>;
  #size-cells = <0>;

  ltdc_ep0_out: endpoint@0 {
   reg = <0>;
   remote-endpoint = <&sii9022_in>;
  };
 };
};

&ltdc {
    port {
        #address-cells = <1>;
        #size-cells = <0>;

        ltdc_ep1_out: endpoint@1 {
            reg = <1>;
            remote-endpoint = <&dsi_panel_in>;--作为connector endpoint对应的panel为dsi_panel_in。
        };
    };
};

4.2 STM32 LTDC Panel初始化

STM32 LTDC提供RGB并口信号、horizontal/vertical同步信号、时钟、数据使能等信号。下面是其初始化:

  • 指定drm_driver,然后创建drm_device并初始化。
  • 分配并初始化drm_bridge/drm_connector。
  • 分配并初始化drm_encoder。
  • 分配并初始化drm_crtc/drm_plane。
  • 依次对plane/crtc/encoder/connector进行reset。
  • 将drm_device注册到DRM子系统。
  • 配置DRM fbdev设备。
stm_drm_platform_probe
  drm_dev_alloc--分配一个DRM设备struct drm_device,驱动使用drv_driver。
  drv_load
    ->drm_mode_config_init--初始化drm_device的mode_config,并对各种属性创建默认值或取值范围等。
    ->ltdc_load
      ->of_graph_get_endpoint_count--获取当前ltdc的端点数。
      ->devm_clk_get--获取lcd时钟并使能。
      ->遍历endpoint子节点。
        ->drm_of_find_panel_or_bridge--根据当前endpoint查找remote-endpoint,返回连接的panel或者bridge。
          ->of_drm_find_panel--遍历panel_list,如找到则返回struct drm_panel。
          ->of_drm_find_bridge--遍历bridge_list,如找到则返回struct drm_bridge。
        ->drm_panel_bridge_add--分配并初始化一个bridge,连接类型为DRM_MODE_CONNECTOR_DPI。建立panel<->bridge之间的关系。
          ->drm_bridge_add--将bridge加入到bridge_list中。
        ->ltdc_encoder_init
          ->drm_encoder_init--初始化一个encoder,将encoder加入到drm_device的encoder_list上。
          ->drm_encoder_helper_add--初始化encoder的帮助函数。
          ->drm_bridge_attach--将bridge附着到初始化好的encoder。建立bridge<->encoder之间的关系。
      ->devm_reset_control_get_exclusive
      ->ltdc_get_caps--读取寄存器判断LTDC的特性。
      ->遍历注册中断。
        ->platform_get_irq--获取中断号。
        ->devm_request_threaded_irq--中断处理函数ltdc_irq和线程化处理函数ltdc_irq_thread。60Hz刷新率,则每16ms一次中断。
          ->ltdc_irq--读取中断状态。
          ->ltdc_irq_thread--根据中断状态进行处理。
            ->drm_crtc_handle_vblank--如果是ISR_LIF,则需要进行vblank处理。
              ->drm_crtc_handle_vblank
                ->drm_handle_vblank--处理vblank事件。
                  ->drm_update_vblank_count
                  ->drm_handle_vblank_events
      ->ltdc_crtc_init
        ->ltdc_plane_create--创建DRM_PLANE_TYPE_PRIMARY plane。
          ->drm_universal_plane_init--初始化drm_plane,操作函数为drm_plane_funcs。
          ->drm_plane_helper_add--帮助函数为ltdc_plane_helper_funcs。
        ->drm_crtc_init_with_planes--初始化CRTC并赋值primary和cursor,操作函数集为ltdc_crtc_funcs。建立plane<->crtc之间的关系。
        ->drm_crtc_helper_add--CRTC帮助函数集为ltdc_crtc_helper_funcs。
        ->drm_mode_crtc_set_gamma_size
        ->drm_crtc_enable_color_mgmt
        ->使用更多layer作为DRM_PLANE_TYPE_OVERLAY Plane。
      ->drm_vblank_init--初始化CRTC的vblank队列、定时器等。
      ->pinctrl_pm_select_sleep_state--根据当前低功耗状态,设置pinctrl。
    ->drm_mode_config_reset--依次调用所有plane、crtc、encoder、connector的reset函数。
    ->drm_kms_helper_poll_init--初始化查询connector的帮助函数。
    ->platform_set_drvdata
  drm_dev_register--注册一个DRM设备。
  drm_fbdev_generic_setup--如果打开CONFIG_DRM_FBDEV_EMULATION,则创建一个模拟FrameBuffer设备。

drv_driver是用户空间对DRM设备文件操作的入口,

static struct drm_driver drv_driver = {
    .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
    .name = "stm",
    .desc = "STMicroelectronics SoC DRM",
    .date = "20170330",
    .major = 1,
    .minor = 0,
    .patchlevel = 0,
    .fops = &drv_driver_fops,--DRM设备文件操作函数集。
    .dumb_create = stm_gem_cma_dumb_create,
    .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
    .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
    .gem_free_object_unlocked = drm_gem_cma_free_object,
    .gem_vm_ops = &drm_gem_cma_vm_ops,
    .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
    .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
    .gem_prime_vmap = drm_gem_cma_prime_vmap,
    .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
    .gem_prime_mmap = drm_gem_cma_prime_mmap,
    .get_scanout_position = ltdc_crtc_scanoutpos,
    .get_vblank_timestamp = drm_calc_vbltimestamp_from_scanoutpos,
};

根据mmap所使用的分配内存方式不同,分为三种:

  • DEFINE_DRM_GEM_FOPS:使用普通GEM内存。
  • DEFINE_DRM_GEM_CMA_FOPS:使用CMA作为GEM对象内存分配器。
  • DEFINE_DRM_GEM_SHMEM_FOPS:使用shmem作为GEM对象内存分配器。

5 Backlight驱动(pwm-backlight)

backlight类初始化:

backlight_class_init
  class_create--创建backlight类。
  ->backlight类设备属性。
  ->初始化backlight设备列表等。

每一个backlight类设备都会创建如下属性:

static struct attribute *bl_device_attrs[] = {
    &dev_attr_bl_power.attr,
    &dev_attr_brightness.attr,
    &dev_attr_actual_brightness.attr,
    &dev_attr_max_brightness.attr,
    &dev_attr_scale.attr,
    &dev_attr_type.attr,
    NULL,
};
ATTRIBUTE_GROUPS(bl_device);

如下是backlight类设备的属性实例:

/sys/class/backlight/panel-backlight/
|-- actual_brightness--当前实际backlight等级。
|-- bl_power--power模式,0-full on;1-3 power saving;4-full off。
|-- brightness--可设置或读取backlight等级。
|-- max_brightness--backlight最大等级。
|-- scale--显示当前backlight是linear还是non-linear。
|-- type--当前backlight类型:raw、platform、firmware。
`-- uevent

一个使用pwm-backlight的驱动dts实例:

    panel_backlight: panel-backlight {
        compatible = "pwm-backlight";
        pwms = <&pwm4 1 5000000>;
        brightness-levels = <0 4 8 16 32 64 128 255>;--亮度等级列表,对应设置到pwm设备中。
        power-supply = <&v3v3>;
        default-brightness-level = <7>;--默认亮度等级。
        status = "okay";
    };

STM32 pwm backlight驱动:

pwm_backlight_probe
  pwm_backlight_parse_dt--解析子节点属性,获取pwms、power、brightness等属性。
  devm_regulator_get--获取panel的power-supply。
  devm_pwm_get--获取panel的pwms。
  pwm_init_state--获取pwm初始状态。
  pwm_apply_state--设置pwm状态。
  backlight_device_register--注册一个backlight_device类设备,操作函数集为pwm_backlight_ops。
  backlight_update_status

6 Panel驱动(panel simple)

panel_simple表示一个panel设备,包括panele在DRM子系统中表示、背光、复位等信息。

struct panel_simple {
    struct drm_panel base;
    bool prepared;
    bool enabled;
    bool no_hpd;
    const struct panel_desc *desc;
    struct backlight_device *backlight;--对应的背光设备。
    struct regulator *supply;
...
};

panel simple dts配置如下:

  • 通过compatible和panel_simple驱动绑定。
  • 指定backlight。
  • 将panel和connector endpoint绑定。
panel_rgb: panel-rgb {
  compatible = "alientek,lcd-rgb";
  backlight = <&panel_backlight>;--背光使用的是panel_backlight,一种pwm backlight。
  status = "okay";
  port {
    dsi_panel_in: endpoint {
      remote-endpoint = <&ltdc_ep1_out>;--connector对应的endpoint。
    };
  };
 };

 panel_simple支持platform_driver和mipi_dsi_driver两种驱动。platform_driver的probe做如下工作:

  • 电源和GPIO获取。
  • backlight获取。
  • panel时序配置获取。
  • 初始化drm_panel并添加到panel_list中。
panel_simple_init
  ->panel_simple_platform_driver
    ->panel_simple_platform_probe
      ->panel_simple_probe
        ->从dts中解析panel属性,包括enable、backlight、panel-timing等。
        ->drm_panel_init
        ->drm_panel_add--将当前struct drm_panel加入到panel_list中。panel操作函数集为panel_simple_funcs。

panel_simple_funcs是跟随drm_panel注册到DRM子系统的操作函数集,在panel被使能关闭时调用:

static const struct drm_panel_funcs panel_simple_funcs = {
    .disable = panel_simple_disable,
    .unprepare = panel_simple_unprepare,
    .prepare = panel_simple_prepare,
    .enable = panel_simple_enable,
    .get_modes = panel_simple_get_modes,
    .get_timings = panel_simple_get_timings,
};

7 DRM相关调试

7.1 DRM相关sysfs节点

关于对DRM子系统跟踪和调试,参考《DRM KMS overview - How to trace and debug the framework》。

drm模块提供参数debug,可以配置输出不同DRM子模块日志:

/sys/module/drm/parameters/debug

或者通过设置bootargs参数drm.debug=0x1ff,则打开全部log。

#define DRM_UT_NONE        0x00
#define DRM_UT_CORE        0x01
#define DRM_UT_DRIVER        0x02
#define DRM_UT_KMS        0x04
#define DRM_UT_PRIME        0x08
#define DRM_UT_ATOMIC        0x10
#define DRM_UT_VBL        0x20
#define DRM_UT_STATE        0x40
#define DRM_UT_LEASE        0x80
#define DRM_UT_DP        0x100

或者修改drb_debug默认值:

unsigned int drm_debug = DRM_UT_CORE|DRM_UT_DRIVER|DRM_UT_KMS|DRM_UT_PRIME|DRM_UT_ATOMIC|DRM_UT_STATE|DRM_UT_LEASE|DRM_UT_DP;
EXPORT_SYMBOL(drm_debug);

 DRI调试节点:

/sys/kernel/debug/dri/0
|-- DPI-1
|   |-- edid_override
|   `-- force
|-- clients
|-- crtc-0
|-- framebuffer
|-- gem_names
|-- internal_clients
|-- name
`-- state

8 DRM相关udev

DRM相关udev存放于/lib/udev/rules.d/60-drm.rules:

# do not edit this file, it will be overwritten on update

ACTION!="remove", SUBSYSTEM=="drm", SUBSYSTEMS=="pci|usb|platform", IMPORT{builtin}="path_id"

# by-path
ENV{ID_PATH}=="?*", KERNEL=="card*", SYMLINK+="dri/by-path/$env{ID_PATH}-card"
ENV{ID_PATH}=="?*", KERNEL=="controlD*", SYMLINK+="dri/by-path/$env{ID_PATH}-control"
ENV{ID_PATH}=="?*", KERNEL=="renderD*", SYMLINK+="dri/by-path/$env{ID_PATH}-render"

参考文档:

DRM KMS overview - stm32mpu (stmicroelectronics.cn)》:关于DRM系统框架图、内核配置、测试工具(modetest/kmscube)、跟踪和调试、用户空间libdrm等介绍。

Linux图形显示系统之DRM - 沉默的思想 - 博客园 (cnblogs.com)

【DRM】DRM Display Driver Guide - 知乎 (zhihu.com)

深入讲解DRM架构介绍(一) - 哔哩哔哩 (bilibili.com)

posted on 2024-02-24 23:59  ArnoldLu  阅读(2209)  评论(0编辑  收藏  举报

导航