Rockchip RK3399 - DRM crtc基础知识

本文介绍了Rockchip RK3399中DRM(Direct Rendering Manager)crtc的基本知识,涵盖了硬件原理,包括显示器的工作方式、时序参数的解析;核心数据结构,如DRM设备结构、显示模式结构和状态结构;以及crtc的操作函数,如模式设置和显示控制。通过对这些内容的理解,有助于深入掌握显示系统的运作机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、LCD硬件原理

1.1 CRT介绍

CRT是阴极射线管(Cathode Ray Tube)的缩写,它是一种使用电子束在荧光屏上创建图像的显示设备。CRT显示器在过去很长一段时间内是主流的显示技术,现已被液晶显示屏或其他新兴技术所替代。

CRT显示器中,扫描电子束从左到右、从上到下移动,照亮屏幕上的荧光点,从而创建图像。电子束每秒多次扫描整个屏幕,产生闪烁效果,需要与正在显示的内容同步。

随着LCDLEDOLED等新型显示技术的出现,底层原理已经发生了变化。这些显示器不再使用扫描电子束,而是使用可以单独控制发光或阻挡光的像素矩阵。这使得图像更加清晰,刷新率更快,能源效率也得到了提高。

然而,刷新显示的基本概念并没有发生很大变化。显示内容仍然通过向每个像素发送信号来控制其亮度和颜色来进行更新。这些信号由显示控制器根据输入视频信号生成,并且它们确定屏幕上显示的内容。

1.2 LCD示意图

下图是LCD示意图,里面的每个点就是一个像素点。它里面有一个电子枪,一边移动,一边发出各种颜色的光。用动态图表示如下:

img

电子枪是如何移动的?

  • 有一条Pixel Clock时钟线与LCD相连,每发出一次Pixel Clock,电子枪就移动一个像素。

颜色如何确定?

  • 由连接LCD的三组线RGB三原色混合而成:R(Red)、G(Green)、B(Blue)确定。

电子枪如何得知应跳到下一行?

  • 有一条HSYNC信号线与LCD相连,每发出一次脉冲(高低电平),电子枪就跳到下一行,该信号叫做行同步信号。

电子枪如何得知应跳到原点?

  • 有一条VSYNC信号线与LCD相连,每发出一次脉冲(高低电平),电子枪就跳到原点,该信号叫做帧同步信号。

RGB线上的数据从何而来?

  • 内存里面划分一块显存(FrameBuffer),里面存放了要显示的数据,LCD控制器(比如RK3399VOP)从里面将数据读出来,通过显示接口(比如mipilvdshdmiedp、``dp)传给电子枪,电子枪再依次打到显示屏上。
img

因此要想在显示设备上显示图像,至少需要3个信号:

  • Pixel Clock:时钟信号,在每个时钟周期更新屏幕上一个像素;
  • HSYNC:水平同步信号,引脚每发出一个脉冲,表示一行的数据开始发送;
  • VSYNC:垂直同步信号,引脚每发出一个脉冲,表示一帧的数据开始发送。
1.3 时序参数

在上图中我们发现除了Pixel ClockHSYNCVSYNC信号外,还包含了大量的时序参数,这里我们一一介绍:

  • HBPD(hback porch):行同步信号的后肩,单位为1个Pixel Clock时钟周期;
  • HFPD(hfront porch):行同步信号的前肩,单位为1个Pixel Clock时钟周期;
  • HSPW(hsync pulse):行同步信号的脉宽,单位为1个Pixel Clock时钟周期;
  • HOZVAL(hdisplay):LCD的水平宽度;
  • VBPD(vback porch):帧同步信号的后肩,单位为1个HSYNC时钟周期;
  • VFPD(vfront porch):帧同步信号的前肩,单位为1个HSYNC时钟周期;
  • VSPW(vsync pulse):帧同步信号的脉宽,单位为1个HSYNC时钟周期;
  • LINEVAL (vdisplay):LCD的垂直宽度;

如果这部分内容看不到,可以参考我之前写的这篇文章:Mini2440裸机开发之LCD基础

二、CRTC核心数据结构

DRM框架中,CRTCFramebuffer中读取待显示的图像,并按照响应的格式输出给encoder,其主要承担的作用为:

  • 配置适合显示的显示模式、分辨率、刷新率等参数,并输出相应的时序;
  • 扫描Framebuffer发送到一个或多个显示器;
  • 更新Framebuffer

概括下就是,对显示器进行扫描,产生时序信号的模块、负责帧切换、电源控制、色彩调整等等。

2.1 struct drm_crtc

linux内核使用struct drm_crtc来表示一个CRTC控制器,包括显示模式、分辨率、刷新率等参数。定义在include/drm/drm_crtc.h

/**
 * struct drm_crtc - central CRTC control structure
 *
 * Each CRTC may have one or more connectors associated with it.  This structure
 * allows the CRTC to be controlled.
 */
struct drm_crtc {
        /** @dev: parent DRM device */
        struct drm_device *dev;
        /** @port: OF node used by drm_of_find_possible_crtcs(). */
        struct device_node *port;
        /**
         * @head:
         *
         * List of all CRTCs on @dev, linked from &drm_mode_config.crtc_list.
         * Invariant over the lifetime of @dev and therefore does not need
         * locking.
         */
        struct list_head head;

        /** @name: human readable name, can be overwritten by the driver */
        char *name;

        /**
         * @mutex:
         *
         * This provides a read lock for the overall CRTC state (mode, dpms
         * state, ...) and a write lock for everything which can be update
         * without a full modeset (fb, cursor data, CRTC properties ...). A full
         * modeset also need to grab &drm_mode_config.connection_mutex.
         *
         * For atomic drivers specifically this protects @state.
         */
        struct drm_modeset_lock mutex;

        /** @base: base KMS object for ID tracking etc. */
        struct drm_mode_object base;

        /**
         * @primary:
         * Primary plane for this CRTC. Note that this is only
         * relevant for legacy IOCTL, it specifies the plane implicitly used by
         * the SETCRTC and PAGE_FLIP IOCTLs. It does not have any significance
         * beyond that.
         */
        struct drm_plane *primary;

        /**
         * @cursor:
         * Cursor plane for this CRTC. Note that this is only relevant for
         * legacy IOCTL, it specifies the plane implicitly used by the SETCURSOR
         * and SETCURSOR2 IOCTLs. It does not have any significance
         * beyond that.
         */
        struct drm_plane *cursor;
        /**
         * @index: Position inside the mode_config.list, can be used as an array
         * index. It is invariant over the lifetime of the CRTC.
         */
        unsigned index;

        /**
         * @cursor_x: Current x position of the cursor, used for universal
         * cursor planes because the SETCURSOR IOCTL only can update the
         * framebuffer without supplying the coordinates. Drivers should not use
         * this directly, atomic drivers should look at &drm_plane_state.crtc_x
         * of the cursor plane instead.
         */
        int cursor_x;
        /**
         * @cursor_y: Current y position of the cursor, used for universal
         * cursor planes because the SETCURSOR IOCTL only can update the
         * framebuffer without supplying the coordinates. Drivers should not use
         * this directly, atomic drivers should look at &drm_plane_state.crtc_y
         * of the cursor plane instead.
         */
        int cursor_y;

        /**
         * @enabled:
         *
         * Is this CRTC enabled? Should only be used by legacy drivers, atomic
         * drivers should instead consult &drm_crtc_state.enable and
         * &drm_crtc_state.active. Atomic drivers can update this by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        bool enabled;

        /**
         * @mode:
         *
         * Current mode timings. Should only be used by legacy drivers, atomic
         * drivers should instead consult &drm_crtc_state.mode. Atomic drivers
         * can update this by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        struct drm_display_mode mode;
        /**
         * @hwmode:
         *
         * Programmed mode in hw, after adjustments for encoders, crtc, panel
         * scaling etc. Should only be used by legacy drivers, for high
         * precision vblank timestamps in
         * drm_crtc_vblank_helper_get_vblank_timestamp().
         *
         * Note that atomic drivers should not use this, but instead use
         * &drm_crtc_state.adjusted_mode. And for high-precision timestamps
         * drm_crtc_vblank_helper_get_vblank_timestamp() used
         * &drm_vblank_crtc.hwmode,
         * which is filled out by calling drm_calc_timestamping_constants().
         */
        struct drm_display_mode hwmode;

        /**
         * @x:
         * x position on screen. Should only be used by legacy drivers, atomic
         * drivers should look at &drm_plane_state.crtc_x of the primary plane
         * instead. Updated by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        int x;
        /**
         * @y:
         * y position on screen. Should only be used by legacy drivers, atomic
         * drivers should look at &drm_plane_state.crtc_y of the primary plane
         * instead. Updated by calling
         * drm_atomic_helper_update_legacy_modeset_state().
         */
        int y;

        /** @funcs: CRTC control functions */
        const struct drm_crtc_funcs *funcs;

        /**
         * @gamma_size: Size of legacy gamma ramp reported to userspace. Set up
         * by calling drm_mode_crtc_set_gamma_size().
         *
         * Note that atomic drivers need to instead use
         * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt().
         */
        uint32_t gamma_size;

        /**
         * @gamma_store: Gamma ramp values used by the legacy SETGAMMA and
         * GETGAMMA IOCTls. Set up by calling drm_mode_crtc_set_gamma_size().
         *
         * Note that atomic drivers need to instead use
         * &drm_crtc_state.gamma_lut. See drm_crtc_enable_color_mgmt().
         */
        uint16_t *gamma_store;
        /** @helper_private: mid-layer private data */
        const struct drm_crtc_helper_funcs *helper_private;

        /** @properties: property tracking for this CRTC */
        struct drm_object_properties properties;

        /**
         * @scaling_filter_property: property to apply a particular filter while
         * scaling.
         */
        struct drm_property *scaling_filter_property;

        /**
         * @state:
         *
         * Current atomic state for this CRTC.
         *
         * This is protected by @mutex. Note that nonblocking atomic commits
         * access the current CRTC state without taking locks. Either by going
         * through the &struct drm_atomic_state pointers, see
         * for_each_oldnew_crtc_in_state(), for_each_old_crtc_in_state() and
         * for_each_new_crtc_in_state(). Or through careful ordering of atomic
         * commit operations as implemented in the atomic helpers, see
         * &struct drm_crtc_commit.
         */
        struct drm_crtc_state *state;

        /**
         * @commit_list:
         *
         * List of &drm_crtc_commit structures tracking pending commits.
         * Protected by @commit_lock. This list holds its own full reference,
         * as does the ongoing commit.
         *
         * "Note that the commit for a state change is also tracked in
         * &drm_crtc_state.commit. For accessing the immediately preceding
         * commit in an atomic update it is recommended to just use that
         * pointer in the old CRTC state, since accessing that doesn't need
         * any locking or list-walking. @commit_list should only be used to
         * stall for framebuffer cleanup that's signalled through
         * &drm_crtc_commit.cleanup_done."
         */
        struct list_head commit_list;

        /**
         * @commit_lock:
         *
         * Spinlock to protect @commit_list.
         */
        spinlock_t commit_lock;
        /**
         * @debugfs_entry:
         *
         * Debugfs directory for this CRTC.
         */
        struct dentry *debugfs_entry;

        /**
         * @crc:
         *
         * Configuration settings of CRC capture.
         */
        struct drm_crtc_crc crc;

        /**
         * @fence_context:
         *
         * timeline context used for fence operations.
         */
        unsigned int fence_context;

        /**
         * @fence_lock:
         *
         * spinlock to protect the fences in the fence_context.
         */
        spinlock_t fence_lock;
        /**
         * @fence_seqno:
         *
         * Seqno variable used as monotonic counter for the fences
         * created on the CRTC's timeline.
         */
        unsigned long fence_seqno;

        /**
         * @timeline_name:
         *
         * The name of the CRTC's fence timeline.
         */
        char timeline_name[32];

        /**
         * @self_refresh_data: Holds the state for the self refresh helpers
         *
         * Initialized via drm_self_refresh_helper_init().
         */
        struct drm_self_refresh_data *self_refresh_data;
};

该结构体包含以下成员变量:

  • dev:该CRTC所属的DRM设备;
  • port:设备节点,被drm_of_find_possible_crtcs()使用;
  • head:链表节点,用于将当前节点添加到&drm_mode_config.crtc_list链表;
  • name:名称,可以被驱动程序覆盖;
  • mutex:用于保护CRTC状态的互斥锁;
  • basestruct drm_mode_object
  • primary:与该CRTC相关的primary plane
  • cursor:与该CRTC相关的cursor plane
  • index:在mode_config.list中的位置,可以用作数组索引;
  • cursor_x:光标的x坐标;
  • cursor_y:光标的y坐标;
  • enabled:指示该CRTC是否已启用;
  • mode:当前模式时序信息;
  • hwmode:硬件中编程的模式;
  • x:在屏幕上的x坐标;
  • y:在屏幕上的y坐标;
  • funcsCRTC控制函数;
  • gamma_size:报告给用户空间的legacy gamma ramp大小;
  • gamma_storelegacy gamma values数据存储区域;
  • helper_private:中间层私有数据;
  • properties:跟踪该CRTC的属性;
  • scaling_filter_property:应用于缩放时的特定滤镜的属性;
  • state:当前CRTC的原子状态;
  • commit_list:跟踪挂起提交的列表;
  • commit_lock:保护commit_list的自旋锁;
  • debugfs_entry:用于调试的目录条目;
  • crcCRC捕获的配置设置;
  • fence_context:用于围栏操作的时间线上下文;
  • fence_lock:保护时间线上的围栏的自旋锁;
  • fence_seqno:用作CRTC时间线上创建的围栏的单调计数器;
  • timeline_nameCRTC时间线的名称;
  • self_refresh_data:保存自刷新辅助程序状态的数据;
2.1.1 struct drm_display_mode

struct drm_display_mode用于表示显示模式,包含了显示的何种时序参数。定义在include/drm/drm_modes.h

/**
 * struct drm_display_mode - DRM kernel-internal display mode structure
 * @hdisplay: horizontal display size
 * @hsync_start: horizontal sync start
 * @hsync_end: horizontal sync end
 * @htotal: horizontal total size
 * @hskew: horizontal skew?!
 * @vdisplay: vertical display size
 * @vsync_start: vertical sync start
 * @vsync_end: vertical sync end
 * @vtotal: vertical total size
 * @vscan: vertical scan?!
 * @crtc_hdisplay: hardware mode horizontal display size
 * @crtc_hblank_start: hardware mode horizontal blank start
 * @crtc_hblank_end: hardware mode horizontal blank end
 * @crtc_hsync_start: hardware mode horizontal sync start
 * @crtc_hsync_end: hardware mode horizontal sync end
 * @crtc_htotal: hardware mode horizontal total size
 * @crtc_hskew: hardware mode horizontal skew?!
 * @crtc_vdisplay: hardware mode vertical display size
 * @crtc_vblank_start: hardware mode vertical blank start
 * @crtc_vblank_end: hardware mode vertical blank end
 * @crtc_vsync_start: hardware mode vertical sync start
 * @crtc_vsync_end: hardware mode vertical sync end
 * @crtc_vtotal: hardware mode vertical total size
 *
 * This is the kernel API display mode information structure. For the
 * user-space version see struct drm_mode_modeinfo.
 *
 * The horizontal and vertical timings are defined per the following diagram.
 *
 * ::
 *
 *
 *               Active                 Front           Sync           Back
 *              Region                 Porch                          Porch
 *     <-----------------------><----------------><-------------><-------------->
 *       //////////////////////|
 *      ////////////////////// |
 *     //////////////////////  |..................               ................
 *                                                _______________
 *     <----- [hv]display ----->
 *     <------------- [hv]sync_start ------------>
 *     <--------------------- [hv]sync_end --------------------->
 *     <-------------------------------- [
Error: arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi:384.15-26 syntax error FATAL ERROR: Unable to parse input tree scripts/Makefile.lib:320: recipe for target 'arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev04.dtb' failed make[2]: *** [arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev04.dtb] Error 1 make[2]: *** 正在等待未完成的任务.... Error: arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi:384.15-26 syntax error FATAL ERROR: Unable to parse input tree scripts/Makefile.lib:320: recipe for target 'arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev06.dtb' failed make[2]: *** [arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev06.dtb] Error 1 Error: arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi:384.15-26 syntax error FATAL ERROR: Unable to parse input tree CHK include/generated/compile.h scripts/Makefile.lib:320: recipe for target 'arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev21.dtb' failed make[2]: *** [arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev21.dtb] Error 1 Error: arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi:384.15-26 syntax error FATAL ERROR: Unable to parse input tree scripts/Makefile.lib:320: recipe for target 'arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev00.dtb' failed make[2]: *** [arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev00.dtb] Error 1 Error: arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi:384.15-26 syntax error FATAL ERROR: Unable to parse input tree scripts/Makefile.lib:320: recipe for target 'arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev07.dtb' failed make[2]: *** [arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev07.dtb] Error 1 Error: arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi:384.15-26 syntax error FATAL ERROR: Unable to parse input tree scripts/Makefile.lib:320: recipe for target 'arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev01.dtb' failed make[2]: *** [arch/arm64/boot/dts/rockchip/rk3399-nanopi4-rev01.dtb] Error 1 scripts/Makefile.build:484: recipe for target 'arch/arm64/boot/dts/rockchip' failed make[1]: *** [arch/arm64/boot/dts/rockchip] Error 2 arch/arm64/Makefile:127: recipe for target 'dtbs' failed make: *** [dtbs] Error 2 make: *** 正在等待未完成的任务....
03-08
<think>嗯,用户遇到了一个编译错误,具体是在编译设备树文件时出现了语法错误。错误信息显示在rk3399-nanopi4-common.dtsi文件的第384行,位置15到26有语法问题。我需要先理解设备树文件的基本结构和常见错误类型。 首先,设备树源文件(.dts或.dtsi)使用一种类似C语言的结构,包含节点、属性和值。常见的语法错误可能包括括号不匹配、分号缺失、属性名拼写错误或者节点定义不正确。用户的问题提示是语法错误,所以需要检查该行附近的代码结构。 查看错误信息的具体位置,384行的第15到26个字符可能存在错误。用户可能需要打开这个文件,定位到该行。可能的错误点包括缺少分号、括号未闭合、属性值格式错误,或者使用了不正确的关键字。 另外,多个版本的设备树文件(如rev04、rev06等)都引用了同一个公共文件(common.dtsi),所以问题可能出在这个公共文件中。一旦修复了common.dtsi中的错误,所有依赖它的文件都会正确编译。 建议用户用编辑器打开该文件,检查第384行的上下文,特别注意括号、分号、逗号的使用是否正确。例如,可能某个属性后面漏掉了分号,或者字符串没有正确闭合。也可能在该行附近有错误的节点定义,比如节点名拼写错误或者属性名不正确。 此外,用户可能在修改设备树文件后未正确清理编译缓存,导致错误依旧存在。可以建议执行make clean后再重新编译,确保没有旧的中间文件影响编译结果。 如果用户不熟悉设备树语法,可能需要参考Rockchip的文档或社区支持,确认该行的写法是否符合规范。有时候,不同版本的内核对设备树的语法要求可能不同,检查是否使用了适合当前内核版本的写法。 总结来说,解决问题的步骤应该是定位错误行,检查并修正语法问题,清理编译环境,然后重新编译。如果用户无法自行解决,可能需要提供具体的代码片段以便进一步分析。</think>这个编译错误提示表明在设备树文件 `rk3399-nanopi4-common.dtsi` 的第 **384行第15到26列** 存在语法错误,导致编译过程中无法解析设备树源文件。以下是问题分析和解决步骤: --- ### **问题原因** 1. **设备树语法错误** - 设备树(Device Tree)文件(`.dtsi` 或 `.dts`)的语法类似于 C 语言,但有自己的规则。常见的语法错误包括: - 括号 `{}` 不匹配 - 分号 `;` 缺失 - 属性值格式错误(如字符串未用双引号包裹) - 节点(Node)或属性(Property)名称拼写错误 - 使用了不兼容的保留字符 2. **错误定位** 错误发生在公共文件 `rk3399-nanopi4-common.dtsi` 的第 **384 行**,具体位置为 **15-26 列**。所有依赖此文件的设备树(如 `rev04.dtb`、`rev06.dtb` 等)均会编译失败。 --- ### **解决步骤** #### 1. **定位错误代码** 打开文件 `arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi`,跳转到第 **384 行**,检查该行附近的代码。 例如,可能的错误代码片段如下: ```dts // 示例:假设第 384 行是以下内容 my_node { compatible = "vendor,device"; reg = <0x1000> // 错误:缺少分号 }; ``` #### 2. **常见语法错误排查** - **检查分号 `;`** 每个属性赋值语句必须以分号结尾,例如 `reg = <0x1000>;`。 - **检查括号闭合** 确保所有节点 `{}` 正确闭合,且没有嵌套错误。 - **检查字符串引号** 字符串属性(如 `compatible`)必须用双引号包裹:`"vendor,device"`。 - **检查符号合法性** 避免在属性名或节点名中使用特殊字符(如 `-`、`_` 需符合命名规则)。 #### 3. **修复后重新编译** 修复语法错误后,执行以下命令清理并重新编译: ```bash # 清理旧编译文件(重要!) make clean # 重新编译设备树 make dtbs ``` --- ### **扩展建议** 1. **对比官方代码库** 如果是自定义修改导致的错误,建议对比官方代码库(如 [FriendlyElec 的 Nanopi4 内核源码](https://github.com/friendlyarm/kernel-rockchip)),确认语法是否一致。 2. **使用 dtc 工具验证** 手动检查设备树文件的语法: ```bash dtc -I dts -O dtb -o /dev/null arch/arm64/boot/dts/rockchip/rk3399-nanopi4-common.dtsi ``` 工具会直接报告语法错误位置。 3. **检查内核版本兼容性** 确保设备树文件与当前编译的内核版本兼容。某些语法可能在新旧内核中存在差异。 --- ### **示例修正** 假设第 384 行的原始错误代码为: ```dts gpio-keys { compatible = "gpio-keys" pinctrl-names = "default"; }; ``` 错误原因是 `compatible` 属性缺少分号,修正后应为: ```dts gpio-keys { compatible = "gpio-keys"; // 补充分号 pinctrl-names = "default"; }; ``` --- 通过以上步骤应能解决设备树语法错误问题。如果仍无法定位,请提供第 **384 行** 的具体代码片段以进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Graceful_scenery

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

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

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

打赏作者

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

抵扣说明:

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

余额充值