一、LCD
硬件原理
1.1 CRT
介绍
CRT
是阴极射线管(Cathode Ray Tube
)的缩写,它是一种使用电子束在荧光屏上创建图像的显示设备。CRT
显示器在过去很长一段时间内是主流的显示技术,现已被液晶显示屏或其他新兴技术所替代。
在CRT
显示器中,扫描电子束从左到右、从上到下移动,照亮屏幕上的荧光点,从而创建图像。电子束每秒多次扫描整个屏幕,产生闪烁效果,需要与正在显示的内容同步。
随着LCD
、LED
和OLED
等新型显示技术的出现,底层原理已经发生了变化。这些显示器不再使用扫描电子束,而是使用可以单独控制发光或阻挡光的像素矩阵。这使得图像更加清晰,刷新率更快,能源效率也得到了提高。
然而,刷新显示的基本概念并没有发生很大变化。显示内容仍然通过向每个像素发送信号来控制其亮度和颜色来进行更新。这些信号由显示控制器根据输入视频信号生成,并且它们确定屏幕上显示的内容。
1.2 LCD
示意图
下图是LCD
示意图,里面的每个点就是一个像素点。它里面有一个电子枪,一边移动,一边发出各种颜色的光。用动态图表示如下:

电子枪是如何移动的?
- 有一条
Pixel Clock
时钟线与LCD
相连,每发出一次Pixel Clock
,电子枪就移动一个像素。
颜色如何确定?
- 由连接
LCD
的三组线RGB
三原色混合而成:R(Red
)、G(Green
)、B(Blue
)确定。
电子枪如何得知应跳到下一行?
- 有一条
HSYNC
信号线与LCD
相连,每发出一次脉冲(高低电平),电子枪就跳到下一行,该信号叫做行同步信号。
电子枪如何得知应跳到原点?
- 有一条
VSYNC
信号线与LCD
相连,每发出一次脉冲(高低电平),电子枪就跳到原点,该信号叫做帧同步信号。
RGB
线上的数据从何而来?
- 内存里面划分一块显存(
FrameBuffer
),里面存放了要显示的数据,LCD
控制器(比如RK3399
的VOP
)从里面将数据读出来,通过显示接口(比如mipi,
lvds,
hdmi、
edp、``dp
)传给电子枪,电子枪再依次打到显示屏上。

因此要想在显示设备上显示图像,至少需要3个信号:
Pixel Clock
:时钟信号,在每个时钟周期更新屏幕上一个像素;HSYNC
:水平同步信号,引脚每发出一个脉冲,表示一行的数据开始发送;VSYNC
:垂直同步信号,引脚每发出一个脉冲,表示一帧的数据开始发送。
1.3 时序参数


在上图中我们发现除了Pixel Clock
、HSYNC
、VSYNC
信号外,还包含了大量的时序参数,这里我们一一介绍:
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
框架中,CRTC
从Framebuffer
中读取待显示的图像,并按照响应的格式输出给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
状态的互斥锁;base
:struct 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坐标;funcs
:CRTC
控制函数;gamma_size
:报告给用户空间的legacy gamma ramp
大小;gamma_store
:legacy gamma values
数据存储区域;helper_private
:中间层私有数据;properties
:跟踪该CRTC
的属性;scaling_filter_property
:应用于缩放时的特定滤镜的属性;state
:当前CRTC
的原子状态;commit_list
:跟踪挂起提交的列表;commit_lock
:保护commit_list
的自旋锁;debugfs_entry
:用于调试的目录条目;crc
:CRC
捕获的配置设置;fence_context
:用于围栏操作的时间线上下文;fence_lock
:保护时间线上的围栏的自旋锁;fence_seqno
:用作CRTC
时间线上创建的围栏的单调计数器;timeline_name
:CRTC
时间线的名称;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.**