DRM 코어 및 디스플레이 (KMS)
Linux DRM(Direct Rendering Manager) 코어 아키텍처와 KMS(Kernel Mode Setting) 디스플레이 파이프라인을 심층 분석합니다. DRM 디바이스 노드와 권한 모델, KMS 객체(CRTC/plane/encoder/connector), atomic modesetting, DRM properties, VRR(Variable Refresh Rate), format modifier, DRM bridge/panel, 디스플레이 연결 프로토콜, HDCP content protection, DRM lease, fbdev 에뮬레이션, 주요 오픈소스 DRM 드라이버 구조를 다룹니다.
DRM (Direct Rendering Manager) 개요
DRM은 Linux 커널의 GPU 접근을 관리하는 서브시스템입니다. 원래 3D 그래픽 가속을 위해 도입되었으나, 현재는 디스플레이 출력(KMS), GPU 메모리 관리(GEM/TTM), GPU 작업 스케줄링까지 포괄하는 핵심 프레임워크로 발전했습니다.
| 구성 요소 | 역할 | 커널 코드 |
|---|---|---|
| DRM Core | 드라이버 등록(Driver Registration), ioctl 디스패치(Dispatch), 파일 오퍼레이션 | drivers/gpu/drm/drm_*.c |
| KMS (Kernel Mode Setting) | 디스플레이 모드 설정, CRTC/Encoder/Connector/Plane | drivers/gpu/drm/drm_atomic*.c |
| GEM (Graphics Execution Manager) | GPU 메모리 버퍼 할당/관리, mmap | drivers/gpu/drm/drm_gem*.c |
| TTM (Translation Table Manager) | VRAM/시스템 메모리 간 버퍼 이동, 페이징 | drivers/gpu/drm/ttm/ |
| DMA-BUF | 디바이스 간 버퍼 공유 (GPU↔카메라↔디스플레이) | drivers/dma-buf/ |
| GPU Scheduler | GPU 작업 큐(Workqueue) 관리, 우선순위(Priority), 타임아웃 처리 | drivers/gpu/drm/scheduler/ |
drivers/video/fbdev/)는 단순 프레임버퍼만 제공하는 레거시 인터페이스입니다.
DRM/KMS는 하드웨어 가속, 다중 디스플레이, vsync, overlay plane 등을 지원하며, fbdev는 DRM으로 대체되는 추세입니다.
메인라인 그래픽 드라이버 개발에서는 DRM/KMS 사용이 권장되며 사실상 표준 경로입니다.
디바이스 노드와 권한 모델
현대 DRM UAPI는 GPU 하나를 단일 문자 디바이스로만 노출하지 않습니다.
같은 하드웨어라도 화면 제어, 비특권 렌더링, 비그래픽 연산을 서로 다른 노드로 나누어
권한 경계와 사용자 공간(User Space) 스택을 분리합니다. 이 구분을 이해하지 못하면 drm_file의 인증 상태, client capability,
버퍼 공유 규칙, compositor와 compute runtime의 역할이 한꺼번에 흐려집니다.
| 노드 종류 | 대표 경로 | 주 용도 | 권한/제약 |
|---|---|---|---|
| Primary | /dev/dri/card0 |
KMS modeset, connector/plane/lease 제어, 레거시 ioctl | DRM master 개념이 존재합니다. 보통 compositor, Xorg, display manager가 소유합니다. |
| Control | /dev/dri/controlD64 |
과거 KMS 제어용으로 설계됨 | 메인라인 문서 기준으로 현재는 사실상 미사용입니다. |
| Render | /dev/dri/renderD128 |
OpenGL/Vulkan/VA-API/OpenCL 등 비특권 렌더링과 GPGPU | modeset 및 privileged ioctl 불가. DRM master 없이 열 수 있으며 PRIME 기반 공유가 중심입니다. |
| Accel | /dev/accel/accel0 |
NPU/AI/신호 처리 같은 비그래픽 compute | DRIVER_COMPUTE_ACCEL 전용 네임스페이스(Namespace)입니다. graphics 스택과 분리된 별도 major를 사용합니다. |
/* 유저 공간은 node 종류와 client capability를 먼저 협상합니다 */
int fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
drmSetClientCap(fd, DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1);
/* para-virtual GPU에서는 커서 plane hotspot capability가 추가로 필요할 수 있습니다 */
drmSetClientCap(fd, DRM_CLIENT_CAP_CURSOR_PLANE_HOTSPOT, 1);
drm_file.atomic과 drm_file.universal_planes가 함께 의미를 가지며,
writeback connector는 atomic 지원 위에 추가로 opt-in 해야 노출됩니다.
DRM 아키텍처
유저 공간 그래픽 스택
DRM/KMS 위에서 동작하는 유저 공간 소프트웨어 스택의 구조입니다. 각 계층이 DRM을 어떻게 활용하는지 이해하면 전체 그래픽 파이프라인을 파악할 수 있습니다.
| 계층 | 구성 요소 | DRM 사용 방식 |
|---|---|---|
| 애플리케이션 | 게임, 브라우저, 3D 뷰어 | OpenGL/Vulkan API 호출 (직접 DRM 접근 안 함) |
| 윈도우 시스템 | Wayland compositor (Mutter, KWin, Weston) | Primary node에서 KMS atomic commit 수행. 렌더링은 GBM + render node 또는 드라이버 전용 UAPI를 함께 사용 |
| GL/VK 드라이버 | Mesa (radeonsi, iris, panfrost, radv, anv) | Render node로 GPU 커맨드 제출. 드라이버 고유 ioctl 사용 |
| libdrm | DRM ioctl 래퍼 라이브러리 | DRM ioctl을 C 함수로 래핑 (drmModeAtomicCommit() 등) |
| GBM (Generic Buffer Manager) | Mesa 제공, compositor용 버퍼 할당 | gbm_bo_create()가 드라이버 backend를 통해 scanout/texture/render target용 BO를 생성. dumb buffer는 이보다 훨씬 제한적인 최소 경로 |
| EGL / WSI | GL 컨텍스트 관리 / Vulkan Window System Integration | DMA-BUF로 렌더 결과를 compositor에 전달 |
- Intel: GL=
iris(Gen8+)/crocus(Gen4~7), Vulkan=anv - AMD: GL=
radeonsi(GCN+), Vulkan=radv - ARM Mali: GL=
panfrost(Midgard/Bifrost)/lima(Utgard), Vulkan=panvk - Qualcomm: GL/Vulkan=
freedreno/turnip - Broadcom: GL=
v3d(Pi4/5)/vc4(Pi0~3), Vulkan=v3dv
DRM 핵심 구조체(Struct)
drm_device & drm_driver
drm_device는 하나의 GPU 디바이스를 나타내고, drm_driver는 해당 GPU 유형의 오퍼레이션을 정의합니다.
#include <drm/drm_drv.h>
#include <drm/drm_gem.h>
#include <drm/drm_ioctl.h>
/* DRM 드라이버 정의 — 드라이버 전체에 하나 */
static const struct drm_driver my_gpu_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC |
DRIVER_RENDER,
/* GEM 오퍼레이션 */
.gem_prime_import = drm_gem_prime_import,
.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table,
/* DRM ioctl 테이블 (드라이버 고유 ioctl) */
.ioctls = my_gpu_ioctls,
.num_ioctls = ARRAY_SIZE(my_gpu_ioctls),
/* /sys, /dev 노드 이름 */
.name = "my-gpu",
.desc = "My GPU DRM driver",
.date = "20260101",
.major = 1,
.minor = 0,
.fops = &my_gpu_fops,
};
drm_file (파일 프라이빗)
유저 프로세스(Process)가 DRM/accel 노드를 open하면 drm_file 구조체가 생성됩니다.
이 구조체는 “프로세스별 GPU 컨텍스트”라기보다 파일 디스크립터별 세션 상태에 가깝고,
인증 여부, master 여부, client capability, 핸들 테이블, 이벤트 큐를 함께 들고 있습니다.
/* drm_file 핵심 필드 발췌 (include/drm/drm_file.h) */
struct drm_file {
bool authenticated; /* 레거시 primary 노드 렌더 권한 */
bool universal_planes;
bool atomic;
bool writeback_connectors;
bool is_master;
bool supports_virtualized_cursor_plane;
struct drm_minor *minor; /* primary/render/accel 중 어떤 노드를 열었는지 */
struct idr object_idr; /* GEM 핸들 → drm_gem_object 매핑 */
struct idr syncobj_idr; /* drm_syncobj 핸들 테이블 */
struct list_head pending_event_list;
struct list_head event_list; /* vblank, page flip, out-fence 이벤트 */
void *driver_priv; /* 드라이버별 프라이빗 데이터 */
};
authenticated 해석 주의:
- primary node에서는 master/authentication이 여전히 의미가 있습니다. KMS 관련 많은 ioctl은 현재 master를 전제로 합니다.
- render node는 modeset/global ioctl을 막고 DRM master 개념도 버립니다. 파일 권한만 맞으면 즉시 GPU 접근이 가능합니다.
- accel node는 graphics stack과 구분된 compute 전용 경로이므로, display 관련 상태를 노출하지 않는 것이 설계 목표입니다.
DRIVER_RENDER를 설정한 드라이버만 render node를 만들고,
DRIVER_COMPUTE_ACCEL는 별도 accel 네임스페이스를 사용합니다.
KMS (Kernel Mode Setting)
KMS는 커널에서 디스플레이 모드를 설정하는 프레임워크입니다. 기존의 유저 공간 모드 설정(UMS)과 달리, 커널이 직접 해상도/주사율/디스플레이 파이프라인을 제어하여 콘솔↔그래픽 전환 시 깜박임이 없고 다중 모니터를 안정적으로 관리합니다.
KMS 핵심 오브젝트
| KMS 오브젝트 | 구조체 | 주요 콜백(Callback) | 역할 |
|---|---|---|---|
| CRTC | drm_crtc |
atomic_check, atomic_flush, atomic_enable |
스캔아웃 엔진. 해상도/주사율 타이밍 생성, Plane 합성 |
| Encoder | drm_encoder |
atomic_check, atomic_enable, atomic_disable |
내부 디지털 신호를 외부 프로토콜(HDMI/DP)로 변환 |
| Connector | drm_connector |
detect, get_modes, atomic_check |
물리적 출력 포트. EDID 파싱, 핫플러그(Hotplug) 감지 |
| Plane | drm_plane |
atomic_check, atomic_update, atomic_disable |
프레임버퍼를 화면에 배치. 스케일링, 회전, 블렌딩 |
| Framebuffer | drm_framebuffer |
create_handle, destroy |
GEM 객체 래퍼. 픽셀 포맷, stride, modifier 정보 |
Vblank 처리와 페이지(Page) 플립
Vblank(수직 귀선 기간)은 디스플레이가 한 프레임을 마치고 다음 프레임을 시작하기 직전의 시간 구간입니다.
이 시점에 프레임버퍼를 교체(page flip)해야 화면 찢어짐(tearing)이 발생하지 않습니다.
DRM 코어는 drm_vblank 인프라로 정확한 vblank 타이밍과 시퀀스 번호를 관리합니다.
/* Vblank 관련 드라이버 콜백 */
static const struct drm_crtc_funcs my_crtc_funcs = {
/* vblank 카운터/타임스탬프 제공 */
.get_vblank_counter = drm_crtc_vblank_count,
.enable_vblank = my_enable_vblank, /* vblank IRQ 활성화 */
.disable_vblank = my_disable_vblank, /* vblank IRQ 비활성화 */
.get_vblank_timestamp = drm_crtc_vblank_helper_get_vblank_timestamp,
};
/* 드라이버 vblank IRQ 핸들러 */
static irqreturn_t my_irq_handler(int irq, void *data)
{
struct drm_device *dev = data;
u32 status = readl(regs + IRQ_STATUS);
if (status & VBLANK_IRQ) {
drm_crtc_handle_vblank(&my_crtc); /* vblank 카운터 증가 + 이벤트 전달 */
}
return IRQ_HANDLED;
}
/* 페이지 플립 완료 알림 (vblank IRQ에서 호출) */
if (page_flip_pending) {
drm_crtc_send_vblank_event(&my_crtc, event);
drm_crtc_vblank_put(&my_crtc);
page_flip_pending = false;
}
| 함수 | 역할 | 호출 위치 |
|---|---|---|
drm_crtc_vblank_on() | CRTC 활성화 시 vblank 추적 시작 | CRTC enable 콜백 |
drm_crtc_vblank_off() | CRTC 비활성화 시 vblank 추적 중단 | CRTC disable 콜백 |
drm_crtc_vblank_get() | vblank IRQ 참조 획득 (카운팅) | 페이지 플립 요청 시 |
drm_crtc_vblank_put() | vblank IRQ 참조 해제 | 플립 완료 후 |
drm_crtc_handle_vblank() | vblank 카운터 증가, 대기 중인 이벤트 처리 | vblank IRQ 핸들러(Handler) |
drm_crtc_send_vblank_event() | 유저에게 페이지 플립 완료 이벤트 전달 | 플립 완료 시점 |
drm_crtc_vblank_helper_get_vblank_timestamp()는
하드웨어 스캔라인 카운터를 읽어 vblank의 정확한 시각을 계산합니다.
이 타임스탬프는 DRM_EVENT_FLIP_COMPLETE 이벤트에 포함되어 유저 공간(Wayland compositor 등)이
프레임 타이밍을 정밀하게 제어하는 데 사용됩니다. 고정밀 프레임 스케줄링의 핵심 인프라입니다.
Atomic Modesetting
Atomic modesetting은 KMS의 현대적 커밋 모델입니다. 여러 디스플레이 속성 변경을 하나의 원자적(Atomic) 트랜잭션(Transaction)으로 묶어 중간 상태 없이 적용하거나 전체를 롤백(Rollback)합니다.
Atomic 상태 객체 그래프
실제 atomic UAPI의 핵심은 “property를 바로 레지스터(Register)에 쓰는 것”이 아니라,
drm_atomic_state 아래에 CRTC/plane/connector/private object의
새 상태를 모아 둔 뒤 한 번에 검증하고 커밋하는 것입니다.
driver의 atomic_check는 이 그래프 전체를 보고 대역폭(Bandwidth), scaler 수, watermark, 링크 훈련 가능 여부,
color LUT 크기, writeback 출력 버퍼 충돌까지 판단합니다.
| 상태 객체 | 대표 구조체 | 주요 내용 |
|---|---|---|
| 트랜잭션 컨테이너(Container) | drm_atomic_state |
이번 커밋에서 바뀌는 모든 object의 old/new state 포인터와 acquire context를 묶습니다. |
| CRTC 상태 | drm_crtc_state |
mode, active 여부, color pipeline, vrr, out-fence, bandwidth 결과를 담습니다. |
| Plane 상태 | drm_plane_state |
framebuffer, source/destination rectangle, rotation, zpos, in-fence를 담습니다. |
| Connector 상태 | drm_connector_state |
연결된 CRTC, link status, colorspace, writeback job, content protection 상태를 담습니다. |
| 드라이버 private 상태 | drm_private_obj 계열 |
watermark, shared DPLL, DSC slice allocator처럼 여러 오브젝트가 공유하는 HW 자원을 모델링합니다. |
/* Atomic 커밋 흐름 (커널 내부) */
/* 1. 유저가 DRM_IOCTL_MODE_ATOMIC 호출 */
/* → drm_mode_atomic_ioctl() 진입 */
/* 2. 상태 복제 */
struct drm_atomic_state *state = drm_atomic_state_alloc(dev);
/* 각 오브젝트의 현재 상태를 복제하여 new_state 생성 */
/* 3. 속성 적용 (유저가 요청한 변경사항) */
drm_atomic_set_crtc_for_connector(new_conn_state, crtc);
new_crtc_state->mode_blob = mode;
new_plane_state->fb = framebuffer;
new_plane_state->crtc = crtc;
/* 4. 검증 (atomic_check) — 하드웨어 제약 확인 */
ret = drm_atomic_check_only(state);
/* 각 CRTC/Plane/Connector의 atomic_check 콜백 호출 */
/* 실패 시 -EINVAL 반환, 유저에게 거부 통보 */
/* 5. TEST_ONLY 플래그면 여기서 종료 (검증만 수행) */
if (flags & DRM_MODE_ATOMIC_TEST_ONLY)
return ret;
/* 6. 커밋 (atomic_commit) — 실제 하드웨어 적용 */
ret = drm_atomic_commit(state);
/* NONBLOCK 플래그: 비동기 커밋 (vsync 대기 안 함) */
/* PAGE_FLIP_EVENT: 완료 시 유저에게 이벤트 전달 */
DRM_MODE_ATOMIC_TEST_ONLY— 검증만 수행, 실제 적용 안 함. 유저 공간에서 구성 유효성을 미리 확인DRM_MODE_ATOMIC_NONBLOCK— 비동기 커밋. vsync을 기다리지 않고 즉시 반환DRM_MODE_PAGE_FLIP_EVENT— 플립 완료 시DRM_EVENT_FLIP_COMPLETE이벤트를 유저에게 전달DRM_MODE_ATOMIC_ALLOW_MODESET— 해상도/주사율 변경이 포함된 full modeset 허용
DRM Properties 시스템
KMS 오브젝트(CRTC, Plane, Connector)의 모든 설정 가능한 속성은 DRM Property로 표현됩니다. Atomic modesetting에서는 property ID와 값을 쌍으로 전달하여 디스플레이 상태를 변경합니다.
Property 타입
| 타입 | 커널 생성 함수 | 값 형태 | 예시 |
|---|---|---|---|
| Range | drm_property_create_range() |
min~max 정수 | alpha (0~0xFFFF), rotation angle |
| Enum | drm_property_create_enum() |
이름↔값 열거 | DPMS (On/Standby/Suspend/Off) |
| Bitmask | drm_property_create_bitmask() |
비트 조합 | rotation (ROTATE_0 | REFLECT_X) |
| Blob | drm_property_create_blob() |
임의 바이너리 데이터 | MODE_ID (drm_mode_modeinfo), EDID |
| Object | drm_property_create_object() |
다른 KMS 오브젝트 ID | CRTC_ID, FB_ID, IN_FENCE_FD |
| Signed Range | drm_property_create_signed_range() |
부호 있는 정수 | SRC_X, SRC_Y (16.16 고정소수점) |
표준 KMS Property
/* === CRTC Properties === */
"ACTIVE" /* bool: CRTC 활성화 (atomic에서 DPMS 대체) */
"MODE_ID" /* blob: 디스플레이 모드 (해상도/주사율) */
"OUT_FENCE_PTR" /* ptr: 커밋 완료 fence fd 반환 위치 */
"VRR_ENABLED" /* bool: 가변 주사율 활성화 */
"DEGAMMA_LUT" /* blob: 디감마 LUT (Color Management) */
"CTM" /* blob: 색 변환 매트릭스 (3x3, S31.32 고정소수) */
"GAMMA_LUT" /* blob: 감마 LUT */
/* === Plane Properties === */
"FB_ID" /* object: 표시할 프레임버퍼 */
"CRTC_ID" /* object: 연결할 CRTC */
"SRC_X/Y/W/H" /* range: 소스 영역 (16.16 고정소수점) */
"CRTC_X/Y/W/H" /* range: 화면 위 위치/크기 (스케일링) */
"rotation" /* bitmask: ROTATE_0/90/180/270 | REFLECT_X/Y */
"alpha" /* range: 투명도 (0=투명, 0xFFFF=불투명) */
"pixel blend mode" /* enum: None/Pre-multiplied/Coverage */
"zpos" /* range: 레이어 순서 (높을수록 위) */
"IN_FENCE_FD" /* range: explicit sync 입력 fence fd */
"COLOR_ENCODING" /* enum: YCbCr BT.601/709/2020 */
"COLOR_RANGE" /* enum: YCbCr Limited/Full Range */
/* === Connector Properties === */
"CRTC_ID" /* object: 연결할 CRTC */
"DPMS" /* enum: On/Standby/Suspend/Off (레거시) */
"link-status" /* enum: Good/Bad — 링크 학습 실패 시 Bad */
"EDID" /* blob: 모니터 EDID 바이너리 (읽기 전용) */
"content type" /* enum: No Data/Graphics/Photo/Cinema/Game */
"max bpc" /* range: 최대 색 깊이 (bits per component) */
"HDR_OUTPUT_METADATA" /* blob: HDR 정적/동적 메타데이터 */
"vrr_capable" /* range: 모니터 VRR 지원 여부 (읽기 전용) */
/* 드라이버에서 커스텀 property 등록 */
struct drm_property *prop;
/* Enum property 생성 */
static const struct drm_prop_enum_list my_scaling_modes[] = {
{ 0, "None" },
{ 1, "Full" },
{ 2, "Center" },
{ 3, "Full aspect" },
};
prop = drm_property_create_enum(dev, 0, "scaling mode",
my_scaling_modes, ARRAY_SIZE(my_scaling_modes));
/* CRTC에 property 연결 */
drm_object_attach_property(&crtc->base, prop, 0);
/* atomic_check에서 property 값 읽기 */
static int my_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state =
drm_atomic_get_new_crtc_state(state, crtc);
/* crtc_state에서 property 값에 접근 */
...
}
modetest -M <driver> -p로 모든 KMS 오브젝트의 property와 현재 값을 확인할 수 있습니다.
유저 공간에서는 DRM_IOCTL_MODE_GETPROPERTY로 property 메타데이터를, DRM_IOCTL_MODE_OBJ_GETPROPERTIES로 오브젝트별 값을 조회합니다.
Color Management 파이프라인
KMS는 CRTC 레벨에서 3단계 색상 처리 파이프라인을 제공합니다. 이 파이프라인으로 HDR 톤매핑, 색 공간 변환, 감마 보정 등을 하드웨어 가속으로 수행합니다.
/* Color Management 관련 구조체 */
/* 감마/디감마 LUT 엔트리 (유저 공간에서 설정) */
struct drm_color_lut {
__u16 red; /* 0~0xFFFF */
__u16 green;
__u16 blue;
__u16 reserved;
};
/* 색 변환 매트릭스 (CTM) — S31.32 고정소수점 */
struct drm_color_ctm {
__u64 matrix[9]; /* 3x3, 부호 비트 + 31.32 고정소수 */
};
/* 유저 공간에서 감마 LUT 설정 예시 (libdrm) */
struct drm_color_lut lut[256];
for (int i = 0; i < 256; i++) {
/* sRGB 감마 커브: 선형 → sRGB 비선형 */
double v = (double)i / 255.0;
double srgb = (v <= 0.0031308) ?
v * 12.92 : 1.055 * pow(v, 1.0/2.4) - 0.055;
__u16 val = (__u16)(srgb * 0xFFFF);
lut[i].red = lut[i].green = lut[i].blue = val;
}
/* blob property로 설정 */
drmModeCreatePropertyBlob(fd, lut, sizeof(lut), &blob_id);
/* atomic 커밋에 GAMMA_LUT=blob_id 추가 */
HDR_OUTPUT_METADATA connector property로 SMPTE ST 2086 마스터링 디스플레이 정보와
MaxCLL/MaxFALL 값을 설정합니다. PQ(Perceptual Quantizer) EOTF는 Degamma LUT로 처리하며,
BT.2020 색 공간 변환은 CTM으로 수행합니다. 하드웨어가 충분한 LUT 정밀도(최소 1024 엔트리)를 제공해야 정확한 HDR 재현이 가능합니다.
VRR (Variable Refresh Rate) / Adaptive Sync
VRR은 GPU 렌더링 속도에 맞춰 디스플레이 주사율을 동적으로 조절하는 기술입니다. 화면 찢어짐(tearing)과 스터터링(stuttering) 없이 부드러운 프레임 전달을 가능하게 합니다.
| 기술 | 표준 | DRM 지원 | 설명 |
|---|---|---|---|
| Adaptive-Sync | VESA DP Adaptive-Sync | DisplayPort connector | DP 표준의 VRR. FreeSync 모니터가 주로 사용 |
| HDMI VRR | HDMI 2.1 VRR | HDMI connector | HDMI Forum VRR. QFT (Quick Frame Transport) 포함 |
| Panel Self Refresh | eDP PSR/PSR2 | eDP connector | 정적 화면에서 패널이 자체 리프레시 → GPU 절전 |
/* VRR 활성화 흐름 (커널 내부) */
/* 1. Connector가 VRR 지원하는지 확인 */
/* 모니터 EDID의 Adaptive-Sync range 파싱 */
connector->vrr_capable = true; /* 드라이버가 EDID 기반으로 설정 */
/* 2. 유저 공간에서 CRTC의 VRR_ENABLED property 설정 */
/* → atomic commit에 포함 */
new_crtc_state->vrr_enabled = true;
/* 3. 드라이버 atomic_check에서 VRR 설정 검증 */
static int my_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_atomic_state *state)
{
struct drm_crtc_state *crtc_state =
drm_atomic_get_new_crtc_state(state, crtc);
if (crtc_state->vrr_enabled) {
/* VRR 범위 확인 (min_vfreq ~ max_vfreq) */
struct drm_connector *conn = get_connector(crtc_state);
if (!conn->display_info.monitor_range.max_vfreq)
return -EINVAL;
/* 하드웨어 VRR 타이밍 설정 */
my_state->vmin = conn->display_info.monitor_range.min_vfreq;
my_state->vmax = conn->display_info.monitor_range.max_vfreq;
}
return 0;
}
/* 4. 페이지 플립 시 VRR 타이밍 적용 */
/* GPU가 프레임을 완료하면 즉시 vsync 발생 */
/* → 가변 vblank 주기 */
modetest -M amdgpu -p에서vrr_capableproperty 확인cat /sys/class/drm/card0-DP-1/vrr_capable— sysfs에서 직접 조회- Wayland에서는
wp_tearing_control_v1프로토콜로 VRR/tearing 제어 - X11에서는
xrandr --prop으로 VRR 속성 확인
Format Modifier (타일링/압축)
GPU는 메모리 접근 효율을 위해 픽셀 데이터를 단순 행(linear) 방식이 아닌 타일(tile) 형태로 배치합니다.
Format Modifier는 프레임버퍼의 메모리 레이아웃을 기술하는 64비트 토큰입니다.
/* Format Modifier 구조 (include/uapi/drm/drm_fourcc.h) */
/*
* [63:56] = vendor (DRM_FORMAT_MOD_VENDOR_*)
* [55:0] = vendor별 modifier 값
*
* 0 = DRM_FORMAT_MOD_LINEAR (행 순차 배치, 모든 디바이스 공통)
*/
#define DRM_FORMAT_MOD_LINEAR 0
/* Intel 타일링 modifier 예시 */
#define I915_FORMAT_MOD_X_TILED /* X-tiling (레거시) */
#define I915_FORMAT_MOD_Y_TILED /* Y-tiling (Gen9+) */
#define I915_FORMAT_MOD_4_TILED /* Tile-4 (Gen12.5+, DG2/MTL) */
#define I915_FORMAT_MOD_Y_TILED_CCS /* Y-tiling + CCS 압축 */
/* AMD modifier 예시 */
#define AMD_FMT_MOD /* 타일 버전, pipe_xor_bits, DCC 등 인코딩 */
/* ARM AFBC (Arm Frame Buffer Compression) */
#define DRM_FORMAT_MOD_ARM_AFBC /* Mali GPU 프레임버퍼 무손실 압축 */
/* Broadcom */
#define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED /* VideoCore T-tiling */
/* NVIDIA */
#define DRM_FORMAT_MOD_NVIDIA_BLOCK_LINEAR_2D /* GOB 기반 타일링 */
| 레이아웃 | 설명 | 장점 | 단점 |
|---|---|---|---|
| Linear | 행 순차 배치 (pitch × height) | 모든 디바이스 호환, 단순 | GPU 캐시(Cache) 효율 낮음 |
| Tiled | 정사각 또는 직사각 타일 단위 배치 | 2D 공간 지역성 향상, GPU 캐시 효율 | 디바이스별 포맷 비호환 |
| Compressed | 타일링 + 무손실 압축 (CCS, DCC, AFBC) | 메모리 대역폭 절감 30~50% | 디바이스 간 공유 시 압축 해제 필요 |
/* 프레임버퍼 생성 시 modifier 지정 (ADDFB2) */
struct drm_mode_fb_cmd2 cmd = {
.width = 1920,
.height = 1080,
.pixel_format = DRM_FORMAT_XRGB8888,
.handles[0] = gem_handle,
.pitches[0] = pitch,
.modifier[0] = I915_FORMAT_MOD_Y_TILED, /* modifier 지정 */
.flags = DRM_MODE_FB_MODIFIERS, /* modifier 사용 플래그 */
};
drmIoctl(fd, DRM_IOCTL_MODE_ADDFB2, &cmd);
/* Plane이 지원하는 modifier 목록 조회 */
/* DRM_IOCTL_MODE_GETPLANE2 또는 IN_FORMATS blob property */
/* → (format, modifier) 쌍의 목록 반환 */
/* 드라이버: Plane에 지원 modifier 등록 */
static const uint64_t my_modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
I915_FORMAT_MOD_Y_TILED,
I915_FORMAT_MOD_Y_TILED_CCS,
DRM_FORMAT_MOD_INVALID /* 종료 마커 */
};
drm_universal_plane_init(dev, plane, ...,
formats, num_formats, my_modifiers, ...);
DRM_FORMAT_MOD_LINEAR를 사용하거나, 양쪽 디바이스가 모두 지원하는 modifier를 협상해야 합니다.
Wayland에서는 zwp_linux_dmabuf_v1 프로토콜로 compositor와 클라이언트 간 modifier를 협상합니다.
DRM 드라이버 기본 구조
최소한의 KMS DRM 드라이버 (디스플레이만 지원, GPU 가속 없음)의 골격입니다.
#include <linux/module.h>
#include <linux/platform_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_gem_shmem_helper.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/drm_fbdev_shmem.h>
struct my_drm {
struct drm_device drm;
struct drm_simple_display_pipe pipe;
struct drm_connector connector;
void __iomem *regs;
};
/* --- Connector --- */
static int my_connector_get_modes(struct drm_connector *conn)
{
/* 고정 해상도 모드 추가 (실제로는 EDID 파싱) */
return drm_add_modes_noedid(conn, 1920, 1080);
}
static const struct drm_connector_helper_funcs my_conn_helpers = {
.get_modes = my_connector_get_modes,
};
/* --- Simple Display Pipe --- */
static void my_pipe_enable(struct drm_simple_display_pipe *pipe,
struct drm_crtc_state *crtc_state,
struct drm_plane_state *plane_state)
{
struct my_drm *my = container_of(pipe, struct my_drm, pipe);
struct drm_framebuffer *fb = plane_state->fb;
/* 하드웨어 레지스터에 프레임버퍼 주소/포맷 설정 */
writel(drm_fb_dma_get_gem_addr(fb, plane_state, 0),
my->regs + FB_ADDR_REG);
writel(fb->pitches[0], my->regs + FB_PITCH_REG);
/* 디스플레이 엔진 활성화 */
writel(1, my->regs + DISPLAY_ENABLE_REG);
}
static void my_pipe_update(struct drm_simple_display_pipe *pipe,
struct drm_plane_state *old_state)
{
struct drm_plane_state *state = pipe->plane.state;
if (state->fb)
my_pipe_enable(pipe, NULL, state);
}
static const struct drm_simple_display_pipe_funcs my_pipe_funcs = {
.enable = my_pipe_enable,
.update = my_pipe_update,
};
/* --- DRM Driver --- */
DEFINE_DRM_GEM_FOPS(my_fops);
static const struct drm_driver my_driver = {
.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
.fops = &my_fops,
DRM_GEM_SHMEM_DRIVER_OPS,
.name = "my-display",
.desc = "My simple display driver",
.date = "20260101",
.major = 1,
.minor = 0,
};
/* --- Platform Driver --- */
static int my_probe(struct platform_device *pdev)
{
struct my_drm *my;
struct drm_device *drm;
static const uint32_t formats[] = { DRM_FORMAT_XRGB8888 };
int ret;
my = devm_drm_dev_alloc(&pdev->dev, &my_driver,
struct my_drm, drm);
if (IS_ERR(my))
return PTR_ERR(my);
drm = &my->drm;
/* MMIO 매핑 */
my->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(my->regs))
return PTR_ERR(my->regs);
/* Connector 초기화 */
drm_connector_init(drm, &my->connector,
&my_conn_funcs, DRM_MODE_CONNECTOR_Unknown);
drm_connector_helper_add(&my->connector, &my_conn_helpers);
/* Simple display pipe 초기화 (CRTC+Plane+Encoder 한 번에) */
ret = drm_simple_display_pipe_init(drm, &my->pipe,
&my_pipe_funcs, formats, ARRAY_SIZE(formats),
NULL, &my->connector);
if (ret)
return ret;
drm_mode_config_reset(drm);
ret = drm_dev_register(drm, 0);
if (ret)
return ret;
/* fbdev 에뮬레이션 (콘솔 출력) */
drm_fbdev_shmem_setup(drm, 32);
return 0;
}
MODULE_LICENSE("GPL");
주요 오픈소스 DRM 드라이버
| 드라이버 | 하드웨어 | 커널 경로 | 메모리 관리 | 특징 |
|---|---|---|---|---|
| i915 | Intel 내장 GPU | drivers/gpu/drm/i915/ |
GEM + GTT/GGTT | GuC/HuC 펌웨어(Firmware) 지원, Display 13/14, Xe 드라이버로 전환 중 |
| xe | Intel Xe GPU (DG2+) | drivers/gpu/drm/xe/ |
TTM 기반 | i915 후속. 디스크리트 GPU(Arc) 지원, drm_sched 사용 |
| amdgpu | AMD Radeon (GCN+) | drivers/gpu/drm/amd/ |
TTM | DC (Display Core), KFD (HSA 컴퓨트), SR-IOV, RAS |
| nouveau | NVIDIA (리버스 엔지니어링) | drivers/gpu/drm/nouveau/ |
TTM | 커뮤니티 개발, 최신 GPU 지원 제한적, GSP 펌웨어 지원 |
| panfrost | ARM Mali (Midgard/Bifrost) | drivers/gpu/drm/panfrost/ |
GEM shmem | OpenGL ES 지원, drm_sched 사용, MMU 자체 관리 |
| lima | ARM Mali (Utgard) | drivers/gpu/drm/lima/ |
GEM shmem | 레거시 ARM Mali-400/450, OpenGL ES 2.0 |
| vc4 | Broadcom VideoCore IV | drivers/gpu/drm/vc4/ |
GEM CMA | Raspberry Pi 0~3. v3d는 Pi 4/5용 별도 드라이버 |
| msm | Qualcomm Adreno | drivers/gpu/drm/msm/ |
GEM | Freedreno 스택, submitqueue 기반 커맨드 제출 |
| virtio-gpu | 가상 GPU (QEMU/virgl) | drivers/gpu/drm/virtio/ |
GEM shmem | VM 내 3D 가속, virglrenderer로 호스트 GPU 사용 |
| simpledrm | EFI/BIOS 프레임버퍼 | drivers/gpu/drm/tiny/simpledrm.c |
GEM shmem | 부팅 초기 콘솔. UEFI GOP/VBE 프레임버퍼 위에서 동작 |
nvidia-open).
nouveau는 커뮤니티 리버스 엔지니어링 기반으로, 최신 GPU에서는 전력 관리(reclocking)와 성능이 제한됩니다.
최근 커널에서는 GSP(GPU System Processor) 펌웨어를 통해 nouveau의 기능이 개선되고 있습니다.
최근 DRM 코어의 방향
버전 번호 자체보다 중요한 것은 메인라인이 어떤 설계 축으로 이동하는가입니다. 최근 DRM 문서와 헤더를 보면 다음 네 가지 흐름이 거의 모든 드라이버 설계에 공통으로 반영됩니다.
| 축 | 최근 방향 | 실무 의미 |
|---|---|---|
| 권한 분리 | primary/render/accel 노드 역할이 더 명확해짐 | display server, 일반 앱, AI runtime이 같은 fd 모델을 공유하지 않도록 설계해야 합니다. |
| 상태 객체 확장 | atomic property가 색 관리, writeback, HDR/VRR, content protection까지 포괄 | 새 기능은 대개 ioctl 추가보다 property/state 추가로 들어옵니다. |
| 동기화 명시화 | dma_resv, syncobj, explicit sync, timeline fence 사용이 확대 |
render 완료와 KMS 표시 완료를 같은 fence 체인으로 연결하는 능력이 중요해집니다. |
| 복구/관측성 | drm_sched timeout, reset recovery, drm_panic, tracepoint 강화 |
좋은 드라이버는 빠르기만 한 것이 아니라 hang 후에도 원인과 복구 경로를 설명할 수 있어야 합니다. |
drm_panic의 위치: GPU가 패닉 화면을 직접 그리는 기능은 “최근 흐름”의 일부입니다.
하지만 이것은 특정 드라이버가 패닉 시점에도 안전하게 최소 렌더 경로를 유지할 수 있어야 가능한 기능이므로,
일반적인 fast path 설계와는 분리해서 생각해야 합니다.
DRM Bridge & Panel 프레임워크
임베디드/모바일 SoC에서는 디스플레이 출력이 여러 개의 하드웨어 블록 체인으로 구성됩니다.
drm_bridge는 이 체인의 중간 단계(MIPI-DSI to HDMI 변환기, LVDS 직렬화(Serialization)기 등)를,
drm_panel은 최종 디스플레이 패널을 추상화합니다.
/* DRM Bridge 드라이버 구현 예시 */
#include <drm/drm_bridge.h>
static const struct drm_bridge_funcs my_bridge_funcs = {
/* 디스플레이 파이프라인 활성화 순서 */
.pre_enable = my_bridge_pre_enable, /* PLL/클럭 설정 */
.enable = my_bridge_enable, /* 출력 활성화 */
.disable = my_bridge_disable,
.post_disable = my_bridge_post_disable,
/* 모드 검증 */
.mode_valid = my_bridge_mode_valid, /* 지원 해상도 필터링 */
.mode_fixup = my_bridge_mode_fixup, /* adjusted_mode 수정 */
/* Atomic 지원 (권장) */
.atomic_pre_enable = my_bridge_atomic_pre_enable,
.atomic_enable = my_bridge_atomic_enable,
.atomic_disable = my_bridge_atomic_disable,
/* 출력 감지 (DRM_BRIDGE_OP_DETECT) */
.detect = my_bridge_detect,
.get_modes = my_bridge_get_modes, /* EDID 기반 모드 목록 */
.get_edid = my_bridge_get_edid, /* EDID 읽기 */
};
static int my_bridge_probe(struct i2c_client *client)
{
struct my_bridge *mb = devm_kzalloc(&client->dev, sizeof(*mb), GFP_KERNEL);
mb->bridge.funcs = &my_bridge_funcs;
mb->bridge.of_node = client->dev.of_node;
mb->bridge.type = DRM_MODE_CONNECTOR_HDMIA; /* 출력 커넥터 타입 */
mb->bridge.ops = DRM_BRIDGE_OP_DETECT |
DRM_BRIDGE_OP_EDID |
DRM_BRIDGE_OP_MODES;
drm_bridge_add(&mb->bridge);
return 0;
}
/* DRM Panel 드라이버 구현 예시 */
#include <drm/drm_panel.h>
static const struct drm_panel_funcs my_panel_funcs = {
.prepare = my_panel_prepare, /* 전원 ON, 리셋 해제 */
.enable = my_panel_enable, /* 백라이트 ON, 디스플레이 ON */
.disable = my_panel_disable, /* 백라이트 OFF */
.unprepare = my_panel_unprepare, /* 전원 OFF */
.get_modes = my_panel_get_modes, /* 고정 모드 반환 */
};
static int my_panel_probe(struct mipi_dsi_device *dsi)
{
struct my_panel *panel = devm_kzalloc(&dsi->dev, sizeof(*panel), GFP_KERNEL);
drm_panel_init(&panel->panel, &dsi->dev, &my_panel_funcs,
DRM_MODE_CONNECTOR_DSI);
drm_panel_add(&panel->panel);
return 0;
}
/* 디스플레이 드라이버에서 bridge + panel 연결 */
struct drm_bridge *bridge;
bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
/* drm_panel은 자동으로 panel-bridge로 래핑됨 */
drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
/* 체인의 마지막 bridge에서 connector 생성 */
connector = drm_bridge_connector_init(drm, encoder);
ports/port/endpoint 노드로 연결됩니다.
of_graph_get_remote_port_parent()로 연결된 디바이스를 탐색하고,
devm_drm_of_get_bridge()가 DT 그래프를 따라 bridge 또는 panel-bridge를 자동으로 찾습니다.
DRM ioctl 요약
| ioctl | 용도 | 필요 권한 |
|---|---|---|
DRM_IOCTL_VERSION | 드라이버 이름/버전 조회 | 없음 |
DRM_IOCTL_GET_CAP | 기능 조회 (PRIME, ATOMIC 등) | 없음 |
DRM_IOCTL_MODE_GETRESOURCES | CRTC/Connector/Encoder ID 목록 | 없음 |
DRM_IOCTL_MODE_GETCONNECTOR | Connector 상태, 지원 모드 조회 | 없음 |
DRM_IOCTL_MODE_GETENCODER | Encoder 정보 조회 | 없음 |
DRM_IOCTL_MODE_GETCRTC | CRTC 현재 모드 조회 | 없음 |
DRM_IOCTL_MODE_SETCRTC | 모드 설정 (레거시) | DRM Master |
DRM_IOCTL_MODE_ATOMIC | Atomic 모드 설정/페이지 플립 | DRM Master |
DRM_IOCTL_MODE_CREATE_DUMB | Dumb 버퍼 생성 | 없음 |
DRM_IOCTL_MODE_MAP_DUMB | Dumb 버퍼 mmap 오프셋 | 없음 |
DRM_IOCTL_MODE_DESTROY_DUMB | Dumb 버퍼 해제 | 없음 |
DRM_IOCTL_PRIME_HANDLE_TO_FD | GEM → DMA-BUF fd | 없음 |
DRM_IOCTL_PRIME_FD_TO_HANDLE | DMA-BUF fd → GEM | 없음 |
DRM_IOCTL_GEM_CLOSE | GEM 핸들 해제 | 없음 |
DRM_IOCTL_MODE_ADDFB2 | 프레임버퍼 객체 생성 | 없음 |
DRM_IOCTL_MODE_PAGE_FLIP | 페이지 플립 (레거시) | DRM Master |
DRM_IOCTL_SET_CLIENT_CAP | 클라이언트 기능 활성화 (ATOMIC 등) | DRM Master |
DRM Lease (디스플레이 리스)
DRM Lease는 DRM Master가 자신의 KMS 리소스(CRTC, Connector, Plane) 일부를 다른 프로세스에게 독점적으로 위임하는 메커니즘입니다. VR 헤드셋, 멀티시트 디스플레이, 게임 전용 출력 등에서 사용됩니다.
| ioctl | 설명 | 호출자 |
|---|---|---|
DRM_IOCTL_MODE_CREATE_LEASE |
KMS 오브젝트 집합을 리스로 생성, lessee fd 반환 | DRM Master (Lessor) |
DRM_IOCTL_MODE_LIST_LESSEES |
현재 활성 lessee 목록 조회 | DRM Master |
DRM_IOCTL_MODE_GET_LEASE |
리스에 포함된 오브젝트 ID 목록 조회 | Lessee |
DRM_IOCTL_MODE_REVOKE_LEASE |
리스 취소 (오브젝트 회수) | DRM Master |
/* DRM Lease 생성 (유저 공간, libdrm) */
uint32_t objects[] = {
crtc_id, /* 리스할 CRTC */
connector_id, /* 리스할 Connector */
plane_id, /* 리스할 Plane */
};
struct drm_mode_create_lease lease = {
.object_ids = (uint64_t)(uintptr_t)objects,
.object_count = 3,
.flags = O_CLOEXEC | O_NONBLOCK,
};
drmIoctl(master_fd, DRM_IOCTL_MODE_CREATE_LEASE, &lease);
/* lease.lessee_id = 리스 ID */
/* lease.fd = lessee가 사용할 새 DRM fd */
/* Lessee는 lease.fd를 통해 리스된 오브젝트만 접근 가능 */
/* → 독립적인 atomic commit, 페이지 플립 수행 */
wp_drm_lease_device_v1 프로토콜로 compositor가 lease를 제공합니다.
Content Protection (HDCP)
HDCP(High-bandwidth Digital Content Protection)는 디스플레이 출력의 콘텐츠를 암호화(Encryption)하여 무단 복제를 방지하는 DRM(Digital Rights Management) 기술입니다. Linux DRM 서브시스템은 KMS property를 통해 HDCP를 지원합니다.
| HDCP 버전 | 대역폭 | 사용 인터페이스 | 특징 |
|---|---|---|---|
| HDCP 1.4 | TMDS (HDMI 1.x/DP) | HDMI, DP, DVI | 기본 암호화, 리피터 인증 |
| HDCP 2.2 | HDMI 2.0+ | HDMI 2.x | 향상된 암호화(AES-128), LC 검증 |
| HDCP 2.3 | HDMI 2.1/DP 2.x | HDMI 2.1, DP 2.x | 최신 보안 개선 |
/* HDCP KMS Properties (Connector) */
/* "Content Protection" property */
/* 값: Undesired (0) → Desired (1) → Enabled (2) */
/* 유저가 Desired로 설정하면 드라이버가 HDCP 인증 시작 */
/* 성공 시 Enabled로 전환 (읽기 전용) */
/* "HDCP Content Type" property */
/* 값: Type 0 — HDCP 1.4+ 허용 */
/* Type 1 — HDCP 2.2+ 필수 (4K UHD 콘텐츠) */
/* Connector에 HDCP property 등록 (드라이버) */
drm_connector_attach_content_protection_property(connector, true);
/* true = HDCP Content Type property도 함께 등록 */
/* HDCP 인증 흐름 (드라이버 내부) */
/* 1. 유저가 "Content Protection" = Desired 설정 */
/* 2. atomic_check에서 HDCP 요청 감지 */
/* 3. 드라이버가 수신기(모니터)와 HDCP 핸드셰이크 */
/* - AKE_Init / AKE_Send_Cert / AKE_Send_Km ... */
/* 4. 인증 성공 → "Content Protection" = Enabled */
/* 5. 링크 실패/핫플러그 → "Content Protection" = Desired */
/* → 유저 공간에 uevent 통보 → 재인증 시도 */
디스플레이 연결 프로토콜
| 프로토콜 | DRM Connector 타입 | 최대 대역폭 | 특징 |
|---|---|---|---|
| HDMI 2.1 | DRM_MODE_CONNECTOR_HDMIA/B |
48 Gbps | ARC/eARC, VRR, DSC, 4K@120Hz |
| DisplayPort 2.1 | DRM_MODE_CONNECTOR_DisplayPort |
80 Gbps (UHBR20) | MST (데이지체인), DSC, Adaptive-Sync |
| eDP | DRM_MODE_CONNECTOR_eDP |
DP 기반 | 내장 디스플레이 (노트북), PSR (Panel Self Refresh) |
| DSI (MIPI) | DRM_MODE_CONNECTOR_DSI |
레인당 ~4.5 Gbps | 모바일/임베디드 디스플레이, 커맨드/비디오 모드 |
| DPI (RGB) | DRM_MODE_CONNECTOR_DPI |
병렬 전송 | 패럴렐 RGB, 임베디드 LCD 직결 |
| LVDS | DRM_MODE_CONNECTOR_LVDS |
채널당 ~112 MHz | 레거시 노트북/산업용 패널 |
| VGA | DRM_MODE_CONNECTOR_VGA |
~400 MHz (아날로그) | 레거시 아날로그, DAC 필요 |
| USB-C (DP Alt) | DRM_MODE_CONNECTOR_USB |
DP 기반 | USB Type-C를 통한 DP 출력, typec_mux 드라이버 |
DisplayPort MST (Multi-Stream Transport)
DP MST는 하나의 DP 출력에서 여러 디스플레이를 데이지 체인으로 연결하거나 MST 허브를 통해 분기하는 기술입니다.
커널은 drm_dp_mst_topology_mgr로 MST 토폴로지(Topology)를 관리합니다.
| 구성 요소 | 커널 구조체 | 역할 |
|---|---|---|
| Topology Manager | drm_dp_mst_topology_mgr |
MST 허브/브랜치 장치 탐색, 대역폭 할당, 핫플러그 처리 |
| MST Port | drm_dp_mst_port |
브랜치 장치의 각 출력 포트 (downstream connector) |
| Payload | drm_dp_mst_atomic_payload |
각 스트림의 대역폭 할당 (시간 슬롯) |
| VCPI (Virtual Channel Payload ID) | Atomic state 내장 | 가상 채널별 대역폭 크기 (시간 슬롯 수) |
/* MST 토폴로지 매니저 초기화 (드라이버) */
drm_dp_mst_topology_mgr_init(&my->mst_mgr, &my->aux,
16, /* 최대 페이로드 수 */
4, /* 최대 레인 수 */
conn_base_id);
/* MST 허브 감지 시 (HPD IRQ에서) */
drm_dp_mst_hpd_irq_handle_event(&my->mst_mgr, esi);
/* → 토폴로지 변경 시 connector 추가/제거 이벤트 */
/* MST 대역폭 계산 예시 */
/* DP 1.4 HBR3: 8.1 Gbps/레인 × 4 레인 = 32.4 Gbps 총 대역폭 */
/* 4K@60Hz (XRGB8888): ~12.5 Gbps 필요 → 63개 시간 슬롯 중 ~25개 사용 */
/* 나머지 슬롯으로 2번째 모니터 (FHD@60Hz) 연결 가능 */
DSC (Display Stream Compression)
DSC는 VESA가 표준화한 시각적 무손실(visually lossless) 디스플레이 압축입니다.
고해상도(8K, 4K@120Hz+)에서 DP/HDMI 대역폭 한계를 극복하기 위해 사용됩니다.
Linux DRM에서는 drm_dsc_config로 DSC 파라미터를 관리합니다.
| 항목 | 설명 |
|---|---|
| 압축률 | 일반적으로 3:1 (24bpp → 8bpp), 최대 4:1 가능 |
| 슬라이스 | 프레임을 수평 슬라이스로 분할하여 병렬 압축/복원. 슬라이스 수는 모니터 DSC 능력에 의존 |
| 커널 헬퍼 | drm_dsc_compute_rc_parameters()로 Rate Control 파라미터 자동 계산 |
| 협상 | Source(GPU)와 Sink(모니터) 양쪽이 DSC 지원해야 사용 가능. DPCD/EDID에서 DSC 능력 파싱 |
| 활용 예 | 8K@60Hz (48 Gbps 필요 → HDMI 2.1 48 Gbps에 DSC 적용), DP MST에서 대역폭 절감 |
/* DSC 설정 예시 (드라이버 atomic_check에서) */
struct drm_dsc_config dsc_cfg = {};
dsc_cfg.line_buf_depth = 13;
dsc_cfg.bits_per_component = 8;
dsc_cfg.bits_per_pixel = 128; /* 8 bpp × 16 (4비트 소수부) */
dsc_cfg.slice_width = 1920; /* 슬라이스 너비 */
dsc_cfg.slice_height = 108; /* 슬라이스 높이 */
dsc_cfg.pic_width = 3840;
dsc_cfg.pic_height = 2160;
/* RC (Rate Control) 파라미터 자동 계산 */
drm_dsc_compute_rc_parameters(&dsc_cfg);
/* PPS (Picture Parameter Set) 생성 → DP SDP 또는 HDMI infoframe으로 전송 */
drm_dsc_pps_payload_pack(pps_payload, &dsc_cfg);
DRM_MODE_CONNECTOR_WRITEBACK 타입의 가상 커넥터를 지원합니다.
물리 디스플레이 대신 CRTC의 합성 결과를 GEM 버퍼에 기록합니다. 스크린 캡처, 녹화, 가상 디스플레이에 사용되며,
drm_writeback_connector_init()으로 초기화합니다. Atomic commit에서 WRITEBACK_FB_ID와
WRITEBACK_OUT_FENCE_PTR property로 출력 버퍼와 완료 fence를 지정합니다.
fbdev 에뮬레이션
DRM/KMS는 /dev/fb0 (fbdev) 인터페이스를 에뮬레이션하여 레거시 애플리케이션(fbcon, Plymouth 등)과 콘솔 출력을 지원합니다. drm_fbdev_shmem, drm_fbdev_ttm, drm_fbdev_dma 등의 헬퍼가 GEM/TTM 버퍼를 fb_info에 연결하고, DRM atomic 경로로 브리지(Bridge)합니다.
참고 사항
drivers/gpu/drm/— DRM 코어 + 모든 GPU 드라이버include/drm/— DRM 헤더 파일include/uapi/drm/— 유저 공간 API (ioctl, 구조체)Documentation/gpu/— 커널 공식 GPU 문서
- DRM UAPI — primary/render node, client capability, dumb buffer 제약
- DRM KMS — atomic modesetting, plane/connector/CRTC helper 계약
- GPU Driver Documentation — 드라이버별 하위 문서 색인
- DRM Panic — 패닉 화면 출력 설계와 제약
- Linux GPU Driver Developer's Guide — Introduction — DRM 서브시스템 전체 아키텍처 소개
- DRM Internals — DRM 코어 내부 API, 파일 오퍼레이션, ioctl 디스패치
- DRM KMS Helpers — atomic helper, connector/encoder/CRTC helper 함수
- DRM Client — 커널 내부 DRM 클라이언트 (fbdev 에뮬레이션 등)
- VGA Switcheroo — 듀얼 GPU 노트북 전환 메커니즘
- freedesktop.org — libdrm Documentation — libdrm 유저스페이스 라이브러리 API
- Wayland Protocol — Wayland 디스플레이 프로토콜 (KMS 기반)
- VESA Standards — DisplayPort, EDID, DDC 표준 규격
- IGT GPU Tools — DRM/KMS 드라이버 테스트 스위트
- Atomic check 무결성(Integrity) —
atomic_check에서 부작용(side effect) 금지. 검증만 수행하고, 하드웨어 변경은atomic_commit에서 수행 - KMS 핫플러그 — Connector 상태 변경은
drm_kms_helper_hotplug_event()로 유저 공간에 통보. HPD(Hot Plug Detect) IRQ 핸들러에서 호출 - 펌웨어 로딩 — 최신 GPU는 대부분
request_firmware()로 마이크로코드를 로딩합니다. initramfs에 펌웨어 포함 필요 (CONFIG_EXTRA_FIRMWARE)
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.