프레임버퍼 서브시스템 (fbdev)
리눅스 커널 프레임버퍼(fbdev) 서브시스템을 심층 분석합니다.
fb_info·fb_ops 핵심 구조체, fb_var_screeninfo/fb_fix_screeninfo 화면 정보,
픽셀 포맷과 색상 맵, mmap 메모리 매핑, cfb/sys 헬퍼를 통한 하드웨어 가속, 더블 버퍼링과 vsync,
fbcon 콘솔 서브시스템, fb_deferred_io 지연 I/O, efifb/vesafb/simplefb 부트 타임 프레임버퍼,
사용자 공간 ioctl 인터페이스, DRM/KMS와의 비교, 최소 fbdev 드라이버 작성 가이드까지
/dev/fb0의 모든 것을 다룹니다.
/dev/fb0는 캐릭터 디바이스(major 29)입니다.
메모리 매핑을 이해하려면 메모리 관리 기초도 함께 보시면 좋습니다.
핵심 요약
- fb_info — 프레임버퍼 디바이스 하나를 대표하는 최상위 구조체. 화면 정보·콜백·메모리 주소를 모두 담고 있습니다.
- fb_ops — 드라이버가 구현하는 콜백 함수 집합. 그리기, 팬 디스플레이, 모드 설정 등 14개 함수 포인터입니다.
- fb_var / fb_fix_screeninfo — 가변(해상도·색상)과 고정(메모리 주소·라인 길이) 화면 정보를 분리하여 관리합니다.
- fbcon — 프레임버퍼 위에서 동작하는 커널 콘솔. VT(가상 터미널)의 텍스트를 픽셀로 렌더링합니다.
- /dev/fb0 — 사용자 공간에서 프레임버퍼에 접근하는 캐릭터 디바이스.
mmap으로 직접 픽셀을 조작할 수 있습니다.
단계별 이해
- 구조체 이해
fb_info가 중심이고, 그 안에fb_var_screeninfo(가변 정보),fb_fix_screeninfo(고정 정보),fb_ops(콜백)가 연결됩니다. - 콜백 구현
최소한fb_check_var·fb_set_par·fb_setcolreg와 그리기 함수를 구현합니다. - 메모리 설정
비디오 메모리를 할당하고screen_base/fix.smem_start/fix.smem_len을 설정합니다. - 디바이스 등록
register_framebuffer()로 등록하면/dev/fbN이 생성되고, fbcon이 자동으로 연결됩니다.
프레임버퍼 개요
프레임버퍼(framebuffer)는 디스플레이 하드웨어에 표시될 픽셀 데이터를 저장하는 메모리 영역입니다.
리눅스 커널의 fbdev 서브시스템(drivers/video/fbdev/)은 이 메모리 영역에 대한 통일된 인터페이스를 제공하여,
다양한 디스플레이 하드웨어를 하나의 API로 접근할 수 있게 합니다.
역사와 배경
fbdev는 Linux 2.1.x(1997년)에 처음 도입되었습니다. 당시 X Window System 없이도 그래픽 출력이 필요한 임베디드 시스템과, 다양한 아키텍처(m68k, SPARC, PowerPC 등)의 디스플레이를 통일하려는 목적이었습니다. Martin Schaller가 최초 설계를 하고, Geert Uytterhoeven이 Amiga와 Atari 프레임버퍼를 구현하며 프레임워크를 확립했습니다.
디바이스 모델
fbdev는 캐릭터 디바이스(major 29)로 등록됩니다. 각 프레임버퍼 디바이스는 /dev/fb0, /dev/fb1, ... 으로 나타나며,
최대 FB_MAX(기본 32)개까지 지원합니다. 사용자 공간 프로그램은 open()/ioctl()/mmap()/close()로 프레임버퍼에 접근합니다.
진화와 현재 상태
fbdev는 2000년대 중반까지 리눅스의 주요 그래픽 인터페이스였지만, DRM/KMS의 등장으로 "레거시" 인터페이스로 분류됩니다. 그러나 다음 영역에서는 여전히 활발히 사용됩니다:
- 임베디드 시스템 — SPI/I2C 연결 소형 디스플레이 (SSD1306, ILI9341 등)
- 부트 콘솔 — efifb/vesafb/simplefb를 통한 초기 부팅 메시지
- E-Ink 디스플레이 — Deferred I/O 기반 전자 잉크 드라이버
- DRM 호환 계층 —
drm_fbdev_generic_setup()을 통한 fbdev 에뮬레이션
소스 디렉토리 구조
| 경로 | 설명 |
|---|---|
drivers/video/fbdev/core/ | fbdev 코어 (fbmem.c, fbcon.c, fb_defio.c 등) |
drivers/video/fbdev/ | 개별 fbdev 드라이버 (vfb.c, efifb.c, vesafb.c 등) |
include/linux/fb.h | 커널 내부 fbdev 헤더 (fb_info, fb_ops 등) |
include/uapi/linux/fb.h | 사용자 공간 API (ioctl 번호, 구조체) |
Documentation/fb/ | 공식 커널 문서 (framebuffer.rst, api.rst, fbcon.rst 등) |
drivers/video/fbdev/ 디렉토리에는 약 200개 이상의 드라이버가 포함되어 있으며,
코어 프레임워크(core/)는 fbmem.c(~1,800줄), fbcon.c(~3,500줄), fb_defio.c(~300줄) 등으로 구성됩니다.
fbmem.c 내부 디스패치 흐름
fbmem.c는 fbdev 서브시스템의 핵심 파일로, /dev/fbN 캐릭터 디바이스의 file_operations를 구현합니다.
사용자 공간의 시스템 콜이 도착하면, 등록된 fb_info를 찾아 해당 fb_ops 콜백으로 디스패치합니다.
/* fbmem.c — 핵심 file_operations 구조 (단순화) */
static const struct file_operations fb_fops = {
.owner = THIS_MODULE,
.read = fb_read, /* fb_ops->fb_read 또는 기본 구현 */
.write = fb_write, /* fb_ops->fb_write 또는 기본 구현 */
.unlocked_ioctl = fb_ioctl, /* ioctl 디스패치 */
.mmap = fb_mmap, /* fb_ops->fb_mmap 또는 기본 구현 */
.open = fb_open, /* fb_ops->fb_open */
.release = fb_release, /* fb_ops->fb_release */
};
/* 등록된 프레임버퍼 배열 — minor 번호로 인덱싱 */
struct fb_info *registered_fb[FB_MAX]; /* 최대 32개 */
int num_registered_fb; /* 현재 등록된 수 */
FBIOPUT_VSCREENINFO ioctl이 도착하면 fb_set_var()가 호출되며,
내부적으로 (1) fb_check_var()로 파라미터 검증 → (2) fb_set_par()로 하드웨어 설정 →
(3) fb_set_cmap()으로 색상 맵 갱신 → (4) fbcon_event_notify()로 fbcon에 모드 변경 통보
순서로 진행됩니다. 어느 단계에서든 오류가 발생하면 이전 상태로 롤백합니다.
fb_notifier — 이벤트 통보 체인
fbdev는 fb_notifier_call_chain()을 통해 프레임버퍼 상태 변화를 커널의 다른 서브시스템에 알립니다.
fbcon이 이 메커니즘을 사용하여 모드 변경, 블랭킹, 프레임버퍼 등록/해제 등을 감지합니다.
| 이벤트 | 시점 | 주요 수신자 |
|---|---|---|
FB_EVENT_MODE_CHANGE | 해상도/모드 변경 후 | fbcon (레이아웃 재계산) |
FB_EVENT_BLANK | 블랭킹 상태 변경 | fbcon, 백라이트 |
FB_EVENT_FB_REGISTERED | 새 프레임버퍼 등록 | fbcon (바인딩) |
FB_EVENT_FB_UNREGISTERED | 프레임버퍼 해제 | fbcon (분리) |
FB_EVENT_SUSPEND | 시스템 절전 진입 | fbcon (출력 중단) |
FB_EVENT_RESUME | 시스템 절전 복귀 | fbcon (출력 재개) |
fb_info 핵심 구조체
struct fb_info는 fbdev 서브시스템의 중심 구조체입니다. 프레임버퍼 디바이스 하나당 하나의 fb_info 인스턴스가 존재하며,
화면 정보, 드라이버 콜백, 비디오 메모리 주소, 색상 맵, 디바이스 참조 등 모든 것을 담고 있습니다.
fb_info 구조체 정의
/* include/linux/fb.h */
struct fb_info {
/* --- 기본 정보 --- */
int node; /* /dev/fbN의 N (minor 번호) */
int flags; /* FBINFO_* 플래그 */
/* --- 화면 정보 --- */
struct fb_var_screeninfo var; /* 가변 화면 정보 (해상도, 색상, 타이밍) */
struct fb_fix_screeninfo fix; /* 고정 화면 정보 (메모리 주소, 라인 길이) */
/* --- 색상 맵 --- */
struct fb_cmap cmap; /* 현재 색상 맵 (팔레트) */
u32 *pseudo_palette; /* 트루컬러용 의사 팔레트 (16 엔트리) */
/* --- 드라이버 콜백 --- */
const struct fb_ops *fbops; /* 드라이버 오퍼레이션 함수 포인터 */
/* --- 비디오 메모리 --- */
char __iomem *screen_base; /* 가상 주소: CPU가 접근하는 VRAM 시작 */
unsigned long screen_size; /* VRAM 크기 (0이면 fix.smem_len 사용) */
/* --- 커널 내부 --- */
struct device *device; /* 부모 디바이스 (platform_device 등) */
struct device *dev; /* fb_info 자체의 device */
/* --- Deferred I/O --- */
struct fb_deferred_io *fbdefio; /* Deferred I/O 설정 */
/* --- 드라이버 개인 데이터 --- */
void *par; /* 드라이버별 사적 데이터 */
/* ... (일부 필드 생략) ... */
};
fb_var_screeninfo — 가변 화면 정보
"가변"이라는 이름처럼, 사용자 공간에서 FBIOPUT_VSCREENINFO ioctl로 런타임에 변경할 수 있는 정보입니다.
/* include/uapi/linux/fb.h */
struct fb_var_screeninfo {
/* --- 가시 해상도 --- */
__u32 xres; /* 가시 영역 가로 픽셀 수 */
__u32 yres; /* 가시 영역 세로 픽셀 수 */
/* --- 가상 해상도 (더블 버퍼링용) --- */
__u32 xres_virtual; /* 가상 가로 (>= xres) */
__u32 yres_virtual; /* 가상 세로 (>= yres, 2배면 더블 버퍼) */
/* --- 오프셋 (팬 디스플레이) --- */
__u32 xoffset; /* 가시 영역의 가상 내 X 오프셋 */
__u32 yoffset; /* 가시 영역의 가상 내 Y 오프셋 */
/* --- 색상 깊이 --- */
__u32 bits_per_pixel; /* 픽셀당 비트 수 (8, 16, 24, 32 등) */
__u32 grayscale; /* 0 = 컬러, 1 = 그레이스케일 */
/* --- 색상 필드 (비트 레이아웃) --- */
struct fb_bitfield red; /* 빨강 채널: offset, length, msb_right */
struct fb_bitfield green; /* 초록 채널 */
struct fb_bitfield blue; /* 파랑 채널 */
struct fb_bitfield transp; /* 알파/투명도 채널 */
/* --- 하드웨어 가속 --- */
__u32 nonstd; /* 비표준 픽셀 포맷 */
__u32 activate; /* FB_ACTIVATE_* 플래그 */
__u32 accel_flags; /* 가속 플래그 (미사용) */
/* --- 타이밍 (CRTC) --- */
__u32 pixclock; /* 픽셀 클럭 (ps 단위) */
__u32 left_margin; /* HBP (수평 뒷 포치) */
__u32 right_margin; /* HFP (수평 앞 포치) */
__u32 upper_margin; /* VBP (수직 뒷 포치) */
__u32 lower_margin; /* VFP (수직 앞 포치) */
__u32 hsync_len; /* 수평 동기 펄스 길이 */
__u32 vsync_len; /* 수직 동기 펄스 길이 */
__u32 sync; /* FB_SYNC_* 플래그 */
__u32 vmode; /* FB_VMODE_* (인터레이스, 더블스캔 등) */
__u32 rotate; /* 회전 각도 (0, 90, 180, 270) */
__u32 colorspace; /* 색상 공간 */
__u32 reserved[4]; /* 예약 */
};
fb_fix_screeninfo — 고정 화면 정보
"고정"은 드라이버가 초기화 시 설정하고, 사용자 공간에서는 읽기만 가능하다는 의미입니다.
/* include/uapi/linux/fb.h */
struct fb_fix_screeninfo {
char id[16]; /* 디바이스 식별 문자열 (예: "VESA VGA") */
unsigned long smem_start; /* 프레임버퍼 물리 시작 주소 */
__u32 smem_len; /* 프레임버퍼 메모리 크기 (바이트) */
__u32 type; /* FB_TYPE_* (PACKED_PIXELS, PLANES 등) */
__u32 type_aux; /* 인터리브 등 추가 타입 정보 */
__u32 visual; /* FB_VISUAL_* (TRUECOLOR, PSEUDOCOLOR 등) */
__u16 xpanstep; /* X 팬 단위 (0이면 팬 불가) */
__u16 ypanstep; /* Y 팬 단위 */
__u16 ywrapstep; /* Y 래핑 단위 */
__u32 line_length; /* 한 줄의 바이트 수 (stride) */
unsigned long mmio_start; /* MMIO 레지스터 물리 주소 */
__u32 mmio_len; /* MMIO 크기 */
__u32 accel; /* FB_ACCEL_* (가속 하드웨어 타입) */
__u16 capabilities; /* FB_CAP_* (fourcc 지원 등) */
__u16 reserved[2]; /* 예약 */
};
fb_info 생명주기
fb_info는 다음 순서로 생성·등록·해제됩니다:
framebuffer_alloc(size, dev)—fb_info+par(사적 데이터) 할당- 드라이버가
var,fix,fbops,screen_base등을 설정 register_framebuffer(info)—/dev/fbN생성, fbcon 연결- (운영 중 — ioctl, mmap, fbcon 등이
fb_ops호출) unregister_framebuffer(info)— 디바이스 제거framebuffer_release(info)— 메모리 해제
fb_info를 직접 kzalloc()으로 할당하지 마세요.
framebuffer_alloc()은 디바이스 모델 연결, 레퍼런스 카운팅, par 정렬 등을 올바르게 처리합니다.
첫 번째 인자 size는 드라이버 사적 데이터(info->par)의 크기입니다.
FBINFO_* 플래그
fb_info.flags 필드는 프레임버퍼의 능력과 상태를 나타내는 비트 플래그 조합입니다.
드라이버는 probe 시 적절한 플래그를 설정해야 합니다.
| 플래그 | 값 | 의미 |
|---|---|---|
FBINFO_DEFAULT | 0 | 기본값 (특별한 기능 없음) |
FBINFO_HWACCEL_XPAN | 0x1000 | 수평 팬 하드웨어 가속 |
FBINFO_HWACCEL_YPAN | 0x2000 | 수직 팬 하드웨어 가속 (더블 버퍼링) |
FBINFO_HWACCEL_YWRAP | 0x4000 | Y 방향 래핑 가속 |
FBINFO_HWACCEL_COPYAREA | 0x0100 | copyarea 하드웨어 가속 |
FBINFO_HWACCEL_FILLRECT | 0x0200 | fillrect 하드웨어 가속 |
FBINFO_HWACCEL_IMAGEBLIT | 0x0400 | imageblit 하드웨어 가속 |
FBINFO_READS_FAST | 0x0080 | VRAM 읽기가 빠름 (sys 헬퍼 불필요) |
FBINFO_VIRTFB | 0x0004 | 가상 프레임버퍼 (실제 HW 없음) |
FBINFO_PARTIAL_PAN_OK | 0x0040 | 부분 팬 허용 (xpanstep 단위) |
FBINFO_MISC_TILEBLITTING | 0x20000 | 타일 블리팅 지원 (텍스트 모드) |
FBINFO_HWACCEL_COPYAREA를 설정하면 fbcon이 스크롤 시 fb_copyarea()를 적극 활용합니다.
반대로 이 플래그가 없으면 fbcon은 소프트웨어 스크롤(SCROLL_REDRAW)을 선택할 수 있습니다.
FBINFO_HWACCEL_YPAN은 fbcon에게 SCROLL_PAN 모드를 사용하도록 힌트를 줍니다.
fb_ops 콜백 함수
struct fb_ops는 fbdev 드라이버가 구현하는 함수 포인터 집합입니다.
fbmem.c의 파일 오퍼레이션이 사용자 요청을 받으면, 해당 fb_ops 콜백을 호출하여 실제 하드웨어를 제어합니다.
fb_ops 구조체 정의
/* include/linux/fb.h */
struct fb_ops {
struct module *owner;
/* --- 디스플레이 제어 --- */
int (*fb_open)(struct fb_info *info, int user);
int (*fb_release)(struct fb_info *info, int user);
int (*fb_check_var)(struct fb_var_screeninfo *var, struct fb_info *info);
int (*fb_set_par)(struct fb_info *info);
int (*fb_setcolreg)(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info);
int (*fb_setcmap)(struct fb_cmap *cmap, struct fb_info *info);
int (*fb_blank)(int blank, struct fb_info *info);
int (*fb_pan_display)(struct fb_var_screeninfo *var, struct fb_info *info);
/* --- 그리기 (가속) --- */
void (*fb_fillrect)(struct fb_info *info, const struct fb_fillrect *rect);
void (*fb_copyarea)(struct fb_info *info, const struct fb_copyarea *area);
void (*fb_imageblit)(struct fb_info *info, const struct fb_image *image);
/* --- I/O --- */
ssize_t (*fb_read)(struct fb_info *info, char __user *buf,
size_t count, loff_t *ppos);
ssize_t (*fb_write)(struct fb_info *info, const char __user *buf,
size_t count, loff_t *ppos);
int (*fb_mmap)(struct fb_info *info, struct vm_area_struct *vma);
/* --- ioctl --- */
int (*fb_ioctl)(struct fb_info *info, unsigned int cmd, unsigned long arg);
/* --- 커서 (선택) --- */
int (*fb_cursor)(struct fb_info *info, struct fb_cursor *cursor);
/* --- 동기화 --- */
int (*fb_sync)(struct fb_info *info);
/* --- 디버그 --- */
void (*fb_get_caps)(struct fb_info *info, struct fb_blit_caps *caps,
struct fb_var_screeninfo *var);
void (*fb_destroy)(struct fb_info *info);
};
디스플레이 제어 콜백
| 콜백 | 호출 시점 | 역할 | 필수 |
|---|---|---|---|
fb_check_var | FBIOPUT_VSCREENINFO | 요청된 var 검증·조정 (하드웨어 변경 없음) | 권장 |
fb_set_par | check_var 통과 후 | 실제 하드웨어 모드 설정 | 권장 |
fb_setcolreg | 색상 레지스터 설정 | 팔레트/pseudo_palette 하나의 엔트리 설정 | 권장 |
fb_setcmap | FBIOPUTCMAP | 전체 색상 맵 일괄 설정 | 선택 |
fb_blank | FBIOBLANK | 화면 끄기/켜기 (DPMS) | 선택 |
fb_pan_display | FBIOPAN_DISPLAY | 가시 영역 이동 (더블 버퍼 플립) | 선택 |
fb_open | open() | 디바이스 열기 시 초기화 | 선택 |
fb_release | close() | 디바이스 닫기 시 정리 | 선택 |
그리기 콜백
fbcon과 사용자 공간의 그리기 요청을 처리합니다. 하드웨어 가속이 없으면 커널 헬퍼(cfb_* 또는 sys_*)를 사용합니다.
| 콜백 | 역할 | cfb 헬퍼 | sys 헬퍼 |
|---|---|---|---|
fb_fillrect | 사각형 채우기 | cfb_fillrect() | sys_fillrect() |
fb_copyarea | 영역 복사 (스크롤) | cfb_copyarea() | sys_copyarea() |
fb_imageblit | 이미지 전송 (글꼴 렌더링) | cfb_imageblit() | sys_imageblit() |
I/O 콜백
/* 최소 fb_ops 구현 예제 */
static const struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = myfb_check_var,
.fb_set_par = myfb_set_par,
.fb_setcolreg = myfb_setcolreg,
.fb_blank = myfb_blank,
.fb_pan_display = myfb_pan_display,
/* 그리기: cfb 헬퍼 (I/O 메모리 + 하드웨어 가속 없는 경우) */
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
/* mmap은 기본 fbmem.c 구현 사용 (커스텀 불필요 시 생략 가능) */
};
fb_set_par에서 수행하세요.
fb_blank — DPMS 전원 레벨
fb_blank 콜백은 디스플레이 전원 관리(DPMS)를 제어합니다. 5단계 블랭킹 레벨이 정의되어 있으며,
에너지 절약 수준이 높아질수록 디스플레이 복원 시간이 길어집니다.
| 레벨 | 상수 | 값 | H-Sync | V-Sync | 화면 | 복원 시간 |
|---|---|---|---|---|---|---|
| 화면 켜짐 | FB_BLANK_UNBLANK | 0 | ON | ON | ON | 즉시 |
| 일반 블랭크 | FB_BLANK_NORMAL | 1 | ON | ON | OFF (검은색) | 즉시 |
| HSYNC OFF | FB_BLANK_HSYNC_SUSPEND | 2 | OFF | ON | OFF | 짧음 |
| VSYNC OFF | FB_BLANK_VSYNC_SUSPEND | 3 | ON | OFF | OFF | 중간 |
| 완전 꺼짐 | FB_BLANK_POWERDOWN | 4 | OFF | OFF | OFF | 길음 |
그리기 구조체 상세
fb_fillrect, fb_copyarea, fb_imageblit 콜백은 각각 전용 파라미터 구조체를 받습니다:
/* fb_fillrect — 사각형 채우기 파라미터 */
struct fb_fillrect {
__u32 dx; /* 대상 X 좌표 */
__u32 dy; /* 대상 Y 좌표 */
__u32 width; /* 채울 너비 */
__u32 height; /* 채울 높이 */
__u32 color; /* 채울 색상 (pseudo_palette 인덱스 또는 직접 값) */
__u32 rop; /* 래스터 연산: ROP_COPY(0), ROP_XOR(1) */
};
/* fb_copyarea — 영역 복사 파라미터 (스크롤 시 사용) */
struct fb_copyarea {
__u32 dx; /* 대상 X */
__u32 dy; /* 대상 Y */
__u32 width; /* 복사 너비 */
__u32 height; /* 복사 높이 */
__u32 sx; /* 소스 X */
__u32 sy; /* 소스 Y */
};
/* fb_image — 이미지 전송 파라미터 (글꼴 렌더링 시 사용) */
struct fb_image {
__u32 dx; /* 대상 X */
__u32 dy; /* 대상 Y */
__u32 width; /* 이미지 너비 */
__u32 height; /* 이미지 높이 */
__u32 fg_color; /* 전경색 (1bpp 모노크롬 시) */
__u32 bg_color; /* 배경색 */
__u8 depth; /* 이미지 깊이 (1=모노, 8=팔레트 등) */
const char *data; /* 픽셀 데이터 포인터 */
struct fb_cmap cmap; /* 이미지 전용 팔레트 (선택) */
};
fb_fillrect.rop이 ROP_COPY이면 지정 색상으로 덮어쓰고,
ROP_XOR이면 기존 픽셀과 XOR하여 커서 반전 효과를 만듭니다. fbcon은 커서 깜빡임에 ROP_XOR을 사용합니다.
픽셀 포맷과 색상 관리
프레임버퍼의 각 픽셀은 bits_per_pixel만큼의 비트로 구성됩니다.
fb_var_screeninfo의 red, green, blue, transp 필드가 각 채널의 비트 위치와 길이를 정의합니다.
픽셀 비트 레이아웃
색상 필드 설정
/* XRGB8888 색상 필드 설정 예제 */
static void myfb_set_xrgb8888(struct fb_var_screeninfo *var)
{
var->bits_per_pixel = 32;
var->red.offset = 16; var->red.length = 8; var->red.msb_right = 0;
var->green.offset = 8; var->green.length = 8; var->green.msb_right = 0;
var->blue.offset = 0; var->blue.length = 8; var->blue.msb_right = 0;
var->transp.offset = 0; var->transp.length = 0; var->transp.msb_right = 0;
/* RGB565 설정 예시:
* var->bits_per_pixel = 16;
* var->red = (struct fb_bitfield){ .offset = 11, .length = 5 };
* var->green = (struct fb_bitfield){ .offset = 5, .length = 6 };
* var->blue = (struct fb_bitfield){ .offset = 0, .length = 5 };
*/
}
fb_cmap — 색상 맵
팔레트 모드(8bpp 이하)에서 색상 인덱스를 실제 RGB 값으로 변환하는 테이블입니다.
/* fb_setcolreg 구현 — 트루컬러 pseudo_palette 설정 */
static int myfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
u32 *pal = info->pseudo_palette;
if (regno >= 16)
return -EINVAL;
/* 16비트로 정규화된 값을 실제 bpp에 맞게 변환 */
red >>= (16 - info->var.red.length);
green >>= (16 - info->var.green.length);
blue >>= (16 - info->var.blue.length);
pal[regno] = (red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset);
if (info->var.transp.length > 0) {
transp >>= (16 - info->var.transp.length);
pal[regno] |= (transp << info->var.transp.offset);
}
return 0;
}
/* fb_cmap 구조체 */
struct fb_cmap {
__u32 start; /* 시작 인덱스 */
__u32 len; /* 엔트리 수 */
__u16 *red; /* 빨강 배열 */
__u16 *green; /* 초록 배열 */
__u16 *blue; /* 파랑 배열 */
__u16 *transp; /* 알파 배열 (NULL 가능) */
};
pseudo_palette
트루컬러 모드(16/24/32bpp)에서 fbcon이 사용하는 16개 엔트리의 "가짜 팔레트"입니다.
fbcon은 콘솔 색상(0~15)을 이 배열에서 찾아 실제 픽셀 값으로 변환합니다.
드라이버는 probe 시 u32 pseudo_palette[16] 배열을 할당하고 info->pseudo_palette에 연결해야 합니다.
| 포맷 | bpp | Visual | pseudo_palette 필요 | setcolreg 역할 |
|---|---|---|---|---|
| RGB565 | 16 | TRUECOLOR | 예 (16개) | pseudo_palette 채움 |
| RGB888 | 24 | TRUECOLOR | 예 (16개) | pseudo_palette 채움 |
| XRGB8888 | 32 | TRUECOLOR | 예 (16개) | pseudo_palette 채움 |
| 8bpp indexed | 8 | PSEUDOCOLOR | 아니오 | 하드웨어 팔레트 설정 |
| FB_TYPE_* | 값 | 설명 |
|---|---|---|
FB_TYPE_PACKED_PIXELS | 0 | 일반 packed 픽셀 (가장 흔함) |
FB_TYPE_PLANES | 1 | 비트플레인 (Amiga 등 레거시) |
FB_TYPE_INTERLEAVED_PLANES | 2 | 인터리브 비트플레인 |
FB_TYPE_TEXT | 3 | 텍스트 모드 |
FB_TYPE_VGA_PLANES | 4 | VGA 비트플레인 |
FB_TYPE_FOURCC | 5 | FourCC 기반 포맷 (DRM 호환) |
| FB_VISUAL_* | 값 | 설명 |
|---|---|---|
FB_VISUAL_MONO01 | 0 | 흑백 (0=흰색, 1=검은색) |
FB_VISUAL_MONO10 | 1 | 흑백 (0=검은색, 1=흰색) |
FB_VISUAL_TRUECOLOR | 2 | 트루컬러 (pseudo_palette 사용) |
FB_VISUAL_PSEUDOCOLOR | 3 | 인덱스 색상 (하드웨어 팔레트) |
FB_VISUAL_DIRECTCOLOR | 4 | 다이렉트컬러 (R/G/B 별도 팔레트) |
FB_VISUAL_STATIC_PSEUDOCOLOR | 5 | 정적 인덱스 색상 (변경 불가) |
fb_bitfield.msb_right가 0이면 리틀 엔디안 비트 순서(기본), 1이면 빅 엔디안 비트 순서입니다.
대부분의 현대 하드웨어는 msb_right = 0을 사용하지만, 일부 임베디드 디스플레이 컨트롤러는 다를 수 있으니 데이터시트를 반드시 확인하세요.
Stride (라인 바이트 길이)와 정렬
fix.line_length(stride)는 프레임버퍼 메모리에서 한 행(row)이 차지하는 바이트 수입니다.
stride ≥ xres × (bpp/8)이며, 하드웨어에 따라 2의 거듭제곱 정렬, 캐시 라인 정렬(64바이트), 또는
특정 배수(예: 128바이트) 정렬이 필요할 수 있습니다. stride가 잘못되면 화면이 비틀어져 보이는 "대각선 깨짐" 현상이 발생합니다.
/* stride 정렬 계산 예제 */
static void myfb_calc_stride(struct fb_info *info)
{
unsigned int min_stride;
unsigned int align = 64; /* 64바이트 정렬 (예시) */
min_stride = info->var.xres * (info->var.bits_per_pixel / 8);
/* ALIGN 매크로: (value + align - 1) & ~(align - 1) */
info->fix.line_length = ALIGN(min_stride, align);
/* 1366×768 32bpp 예시:
* min_stride = 1366 × 4 = 5464
* ALIGN(5464, 64) = 5504
* → 행당 40바이트 패딩 */
}
/* 픽셀 좌표 → 메모리 오프셋 변환 */
static inline unsigned long pixel_offset(struct fb_info *info,
unsigned int x, unsigned int y)
{
/* stride 기반 계산 (xres 대신 line_length 사용!) */
return y * info->fix.line_length +
x * (info->var.bits_per_pixel / 8);
}
픽셀 포맷 변환 유틸리티
서로 다른 픽셀 포맷 간 변환이 필요한 경우, 비트 시프트와 마스킹으로 수행합니다:
/* 픽셀 포맷 변환 유틸리티 */
/* RGB888 → RGB565 변환 (24bpp → 16bpp) */
static inline u16 rgb888_to_rgb565(u32 rgb888)
{
u8 r = (rgb888 >> 16) & 0xFF;
u8 g = (rgb888 >> 8) & 0xFF;
u8 b = rgb888 & 0xFF;
return ((r >> 3) << 11) | /* R: 8bit → 5bit */
((g >> 2) << 5) | /* G: 8bit → 6bit */
((b >> 3)); /* B: 8bit → 5bit */
}
/* RGB565 → XRGB8888 변환 (16bpp → 32bpp) */
static inline u32 rgb565_to_xrgb8888(u16 rgb565)
{
u8 r = (rgb565 >> 11) & 0x1F;
u8 g = (rgb565 >> 5) & 0x3F;
u8 b = rgb565 & 0x1F;
/* 상위 비트로 확장하여 밝기 보존 */
return ((r << 3 | r >> 2) << 16) |
((g << 2 | g >> 4) << 8) |
((b << 3 | b >> 2));
}
/* fb_var_screeninfo에서 픽셀 값 조합 */
static inline u32 fb_pack_pixel(struct fb_var_screeninfo *var,
u8 r, u8 g, u8 b, u8 a)
{
return ((r >> (8 - var->red.length)) << var->red.offset) |
((g >> (8 - var->green.length)) << var->green.offset) |
((b >> (8 - var->blue.length)) << var->blue.offset) |
(var->transp.length ?
((a >> (8 - var->transp.length)) << var->transp.offset) : 0);
}
FB_TYPE_PACKED_PIXELS에서 픽셀 데이터는 네이티브 바이트 순서로 저장됩니다.
리틀 엔디안(x86, ARM LE)에서 XRGB8888 픽셀 0x00FF0000(빨강)은 메모리에 00 00 FF 00 순서로 저장됩니다.
빅 엔디안 시스템에서는 반대이므로, 유저스페이스에서 바이트 단위로 접근할 때는 htole32() 등의 바이트 순서 변환이 필요합니다.
커널 드라이버에서는 일반적으로 CPU 네이티브 순서를 사용하므로 걱정할 필요가 없습니다.
메모리 매핑과 DMA
사용자 공간에서 프레임버퍼 픽셀을 직접 조작하려면 mmap() 시스템 콜로 비디오 메모리를 프로세스 주소 공간에 매핑합니다.
fbmem.c의 기본 fb_mmap 구현은 fix.smem_start부터 fix.smem_len만큼의 물리 메모리를 매핑합니다.
메모리 유형
| 유형 | 주소 | 특징 | 예시 |
|---|---|---|---|
| MMIO VRAM | ioremap() | PCI BAR 등 하드웨어 VRAM | 데스크탑 GPU fbdev |
| DMA coherent | dma_alloc_coherent() | CPU-디바이스 일관성 보장 | 임베디드 LCD 컨트롤러 |
| 시스템 메모리 | vmalloc()/kmalloc() | 소프트웨어 프레임버퍼 | vfb, USB 디스플레이 |
| CMA | dma_alloc_from_contiguous() | 연속 물리 메모리 | ARM SoC 디스플레이 |
fb_mmap 구현
/* 커스텀 fb_mmap 구현 (I/O 메모리) */
static int myfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
unsigned long start = info->fix.smem_start;
unsigned long len = info->fix.smem_len;
unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
if (off + (vma->vm_end - vma->vm_start) > len)
return -EINVAL;
/* Write-Combining 캐시 속성 (VRAM에 적합) */
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
/* 물리 주소를 사용자 가상 주소에 매핑 */
return vm_iomap_memory(vma, start, len);
}
캐시 일관성
메모리 유형에 따라 적절한 캐시 속성을 선택해야 합니다:
- MMIO VRAM:
pgprot_writecombine()— WC(Write-Combining)로 쓰기 합치기 - DMA coherent:
pgprot_noncached()— 캐시 불가 (하드웨어 일관성 보장) - 시스템 메모리: 기본 캐시 사용 가능 (Deferred I/O로 동기화)
DMA coherent 버퍼 할당
/* DMA coherent 프레임버퍼 할당 (임베디드 LCD 컨트롤러) */
static int myfb_alloc_vram(struct myfb_priv *priv)
{
struct device *dev = priv->dev;
size_t size = priv->xres * priv->yres * (priv->bpp / 8);
/* DMA coherent 메모리 할당 → CPU/디바이스 모두 접근 가능 */
priv->vram_virt = dma_alloc_coherent(dev, size,
&priv->vram_phys, GFP_KERNEL);
if (!priv->vram_virt)
return -ENOMEM;
/* fb_info에 연결 */
priv->info->screen_base = priv->vram_virt;
priv->info->fix.smem_start = priv->vram_phys;
priv->info->fix.smem_len = size;
return 0;
}
fb_mmap 구현 시 사용자가 요청한 매핑 범위가 fix.smem_len을 초과하지 않는지 반드시 검증하세요.
검증 없이 remap_pfn_range()를 호출하면 VRAM 영역 밖의 물리 메모리가 노출되어 보안 취약점이 됩니다.
VRAM 크기 계산
프레임버퍼에 필요한 VRAM 크기는 해상도, 색상 깊이, 버퍼 수로 결정됩니다:
| 해상도 | bpp | 싱글 버퍼 | 더블 버퍼 | 트리플 버퍼 |
|---|---|---|---|---|
| 640×480 | 16 | 600 KB | 1.2 MB | 1.8 MB |
| 1280×720 (720p) | 32 | 3.5 MB | 7.0 MB | 10.5 MB |
| 1920×1080 (1080p) | 32 | 7.9 MB | 15.8 MB | 23.7 MB |
| 3840×2160 (4K) | 32 | 31.6 MB | 63.3 MB | 94.9 MB |
/* VRAM 크기 계산 */
static size_t calc_vram_size(unsigned int xres, unsigned int yres,
unsigned int bpp, unsigned int num_buffers,
unsigned int stride_align)
{
size_t stride = ALIGN(xres * (bpp / 8), stride_align);
return stride * yres * num_buffers;
}
/* 예: 1920×1080, 32bpp, 더블 버퍼, 64바이트 정렬
* stride = ALIGN(1920 × 4, 64) = ALIGN(7680, 64) = 7680 (이미 정렬됨)
* VRAM = 7680 × 1080 × 2 = 16,588,800 bytes ≈ 15.8 MB */
CMA (Contiguous Memory Allocator)
ARM/ARM64 SoC의 디스플레이 컨트롤러는 대부분 물리적으로 연속된 메모리를 요구합니다. CMA는 부팅 시 메모리 영역을 예약하고, 디바이스가 필요할 때 연속 물리 메모리를 할당합니다.
/* CMA를 통한 프레임버퍼 할당 */
static int myfb_alloc_cma(struct myfb_priv *priv)
{
struct device *dev = priv->dev;
/* dma_alloc_coherent()는 CMA가 활성화되면 자동으로 CMA 풀에서 할당 */
priv->vram_virt = dma_alloc_coherent(dev, priv->vram_size,
&priv->vram_phys, GFP_KERNEL);
if (!priv->vram_virt) {
dev_err(dev, "CMA 할당 실패 (%zu bytes)\n", priv->vram_size);
return -ENOMEM;
}
/* vram을 0으로 초기화 (검은 화면) */
memset(priv->vram_virt, 0, priv->vram_size);
dev_info(dev, "VRAM: %zu bytes @ phys 0x%pad\n",
priv->vram_size, &priv->vram_phys);
return 0;
}
/* Device Tree CMA 예약 예시:
* reserved-memory {
* #address-cells = <2>;
* #size-cells = <2>;
* ranges;
*
* fb_reserved: framebuffer@80000000 {
* compatible = "shared-dma-pool";
* reg = <0x0 0x80000000 0x0 0x2000000>; // 32 MB
* reusable;
* };
* };
*
* lcd-controller {
* memory-region = <&fb_reserved>;
* };
*/
cma=64M으로 CMA 풀 크기를 설정할 수 있습니다.
4K 해상도 더블 버퍼링에는 최소 64MB가 필요합니다.
/proc/meminfo의 CmaTotal/CmaFree로 CMA 사용 현황을 확인하세요.
dmesg | grep -i cma로 부팅 시 CMA 예약 영역 정보를 확인할 수 있습니다.
하드웨어 가속
fbdev에서 "하드웨어 가속"은 fb_fillrect, fb_copyarea, fb_imageblit 세 가지 그리기 콜백을
하드웨어 블리터(2D 엔진)로 구현하는 것을 의미합니다. 가속이 없는 경우 커널이 제공하는 소프트웨어 헬퍼를 사용합니다.
cfb 헬퍼 (I/O 메모리용)
cfb_fillrect(), cfb_copyarea(), cfb_imageblit()은 I/O 메모리에 매핑된 VRAM에 직접 접근합니다.
writel()/memcpy_toio() 등 I/O 접근 함수를 사용하므로, ioremap()으로 매핑된 VRAM에 적합합니다.
sys 헬퍼 (시스템 메모리용)
sys_fillrect(), sys_copyarea(), sys_imageblit()은 일반 시스템 메모리(vmalloc/kmalloc)에 접근합니다.
memcpy()/memset()을 사용하므로, USB 디스플레이나 SPI LCD 등 시스템 메모리에 섀도 버퍼를 두는 드라이버에 적합합니다.
가속 방식 선택 가이드
드라이버에서 그리기 헬퍼를 선택할 때의 판단 기준입니다:
- VRAM이 PCI BAR / ioremap으로 매핑됐다면 →
cfb_*사용 - 시스템 메모리(vmalloc/kmalloc/DMA coherent)라면 →
sys_*사용 - SoC에 2D 블리터/DMA 엔진이 있다면 → 커스텀 구현 (가장 빠름)
- SPI/I2C/USB 디스플레이라면 →
sys_*+ Deferred I/O
cfb_fillrect과 sys_copyarea를 섞어 사용하면 안 됩니다.
cfb_*는 I/O 접근자(fb_readl()/fb_writel())를 사용하고,
sys_*는 일반 메모리 접근(memcpy)을 사용합니다.
메모리 유형에 맞는 한 세트를 일관되게 사용해야 합니다.
커스텀 가속
/* cfb/sys 헬퍼 사용 vs 커스텀 가속 */
/* 방법 1: cfb 헬퍼 (가속 없음, I/O 메모리) */
static const struct fb_ops simple_cfb_ops = {
.owner = THIS_MODULE,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
/* 방법 2: sys 헬퍼 (가속 없음, 시스템 메모리) */
static const struct fb_ops simple_sys_ops = {
.owner = THIS_MODULE,
.fb_fillrect = sys_fillrect,
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
};
/* 방법 3: 커스텀 HW 가속 (2D blitter) */
static void myfb_fillrect(struct fb_info *info,
const struct fb_fillrect *rect)
{
struct myfb_priv *priv = info->par;
/* 하드웨어 블리터에 fill 명령 전송 */
writel(rect->dx, priv->mmio + BLITTER_DST_X);
writel(rect->dy, priv->mmio + BLITTER_DST_Y);
writel(rect->width, priv->mmio + BLITTER_WIDTH);
writel(rect->height, priv->mmio + BLITTER_HEIGHT);
writel(rect->color, priv->mmio + BLITTER_COLOR);
writel(BLIT_CMD_FILL, priv->mmio + BLITTER_CMD);
/* 완료 대기 */
myfb_wait_idle(priv);
}
| 비교 항목 | cfb_* 헬퍼 | sys_* 헬퍼 | 커스텀 가속 |
|---|---|---|---|
| 메모리 유형 | I/O 메모리 (ioremap) | 시스템 메모리 (vmalloc) | 하드웨어 레지스터 |
| 접근 방식 | writel/memcpy_toio | memcpy/memset | HW 블리터 명령 |
| CPU 부하 | 높음 | 높음 | 낮음 (HW 오프로드) |
| 구현 난이도 | 매우 쉬움 | 매우 쉬움 | 하드웨어 의존 |
| 용도 | PCI VGA, ioremap VRAM | USB/SPI 디스플레이 | SoC 2D 엔진 |
더블 버퍼링과 Pan/Scroll
더블 버퍼링은 화면 깜빡임(tearing)을 방지하는 기법입니다.
가상 해상도(yres_virtual)를 가시 해상도(yres)의 2배로 설정하고,
fb_pan_display로 가시 영역의 시작 오프셋을 전환(flip)합니다.
fb_pan_display 구현
/* fb_pan_display — 가시 영역 시작 오프셋 변경 */
static int myfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct myfb_priv *priv = info->par;
unsigned long offset;
/* 범위 검증 */
if (var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
/* 새 시작 주소 계산 */
offset = var->yoffset * info->fix.line_length +
var->xoffset * (info->var.bits_per_pixel / 8);
/* 하드웨어에 새 시작 주소 설정 */
writel(info->fix.smem_start + offset,
priv->mmio + LCD_BASE_ADDR_REG);
return 0;
}
가상 해상도 설정
더블 버퍼링을 위해 fb_check_var에서 가상 해상도를 허용합니다:
yres_virtual = 2 * yres— 수직 더블 버퍼링 (가장 일반적)xres_virtual = 2 * xres— 수평 스크롤- VRAM 크기가 허용하는 범위 내에서 설정
FBIO_WAITFORVSYNC
/* FBIO_WAITFORVSYNC 구현 — vsync 대기 */
static int myfb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg)
{
struct myfb_priv *priv = info->par;
switch (cmd) {
case FBIO_WAITFORVSYNC: {
u32 crt = 0;
if (copy_from_user(&crt, (void __user *)arg, sizeof(crt)))
return -EFAULT;
/* 다음 vsync 인터럽트 대기 */
return myfb_wait_for_vsync(priv, crt);
}
default:
return -ENOTTY;
}
}
fb_pan_display를 vsync 없이 호출하면 스캔아웃 도중 버퍼가 전환되어
화면이 찢어지는 tearing 현상이 발생합니다. FBIO_WAITFORVSYNC로 vsync를 대기한 후 pan을 수행하거나,
fb_pan_display 구현 내에서 vsync 대기 후 레지스터를 변경하는 것이 좋습니다.
트리플 버퍼링
더블 버퍼링에서 vsync를 기다리는 동안 CPU/GPU가 유휴 상태가 되어 프레임 레이트가 떨어질 수 있습니다. 트리플 버퍼링은 세 번째 버퍼를 추가하여 이 문제를 해결합니다:
- 버퍼 A: 현재 표시 중 (Front)
- 버퍼 B: 다음 vsync에 표시될 프레임 (Ready)
- 버퍼 C: 현재 렌더링 중 (Back)
/* 트리플 버퍼링 설정 */
/* fb_check_var에서 yres_virtual을 3배 허용 */
static int myfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
/* 최대 트리플 버퍼까지 허용 */
if (var->yres_virtual > var->yres * 3)
var->yres_virtual = var->yres * 3;
/* VRAM 범위 확인 */
if (var->yres_virtual * var->xres_virtual *
(var->bits_per_pixel / 8) > info->fix.smem_len) {
var->yres_virtual = info->fix.smem_len /
(var->xres_virtual * (var->bits_per_pixel / 8));
}
return 0;
}
/* 유저스페이스 트리플 버퍼 사용 패턴 */
/* buffer_idx 순환: 0 → 1 → 2 → 0 → ... */
/* var.yoffset = buffer_idx * var.yres;
* ioctl(fd, FBIO_WAITFORVSYNC, &crt);
* ioctl(fd, FBIOPAN_DISPLAY, &var);
* → 렌더링은 (buffer_idx + 1) % 3 에서 계속 */
fbcon — 프레임버퍼 콘솔
fbcon은 프레임버퍼 위에서 동작하는 커널 콘솔 드라이버입니다.
VT(Virtual Terminal) 서브시스템이 출력하는 텍스트를 fb_ops의 그리기 함수를 통해 픽셀로 렌더링합니다.
프레임버퍼가 등록되면 fbcon이 자동으로 해당 프레임버퍼에 바인딩됩니다.
fbcon 동작 원리
- VT가 문자를 출력하면
fbcon의con_putc()/con_putcs()가 호출됩니다. fbcon이 폰트 비트맵에서 해당 문자의 글리프를 찾습니다.fb_imageblit()으로 글리프를 VRAM에 전송합니다.- 스크롤 시
fb_copyarea()로 VRAM 내용을 이동합니다. - 배경은
fb_fillrect()로 채웁니다.
폰트 렌더링
커널에는 여러 빌트인 폰트가 포함되어 있습니다(lib/fonts/):
font_8x8, font_8x16(기본), font_10x18, font_sun12x22 등.
CONFIG_FONT_* 옵션으로 컴파일에 포함할 폰트를 선택합니다.
스크롤 모드
| 모드 | 동작 | 장점 | 단점 |
|---|---|---|---|
SCROLL_MOVE | fb_copyarea()로 VRAM 내 데이터 이동 | 일반적, HW 가속 가능 | 느린 메모리에서 비효율 |
SCROLL_PAN | fb_pan_display()로 오프셋 변경 | VRAM 복사 없음, 매우 빠름 | 큰 가상 해상도 필요 |
SCROLL_WRAP | PAN + 링 버퍼 방식 래핑 | 메모리 효율적 | 래핑 경계 처리 |
SCROLL_REDRAW | 전체 화면 재그리기 | 단순, 항상 동작 | 가장 느림 |
fbcon 커널 파라미터
# fbcon 커널 부트 파라미터 예시
# 특정 VT를 특정 프레임버퍼에 매핑
fbcon=map:0123 # VT0→fb0, VT1→fb1, VT2→fb2, VT3→fb3
# fbcon 폰트 설정
fbcon=font:VGA8x16 # 기본 폰트 지정
# fbcon 스크롤 모드
fbcon=scrollback:0 # 스크롤백 비활성화
# fbcon 완전 비활성화
fbcon=off # fbcon 사용 안 함 (DRM 콘솔만 사용 시)
fbcon=map:으로 VT와 프레임버퍼의 매핑을 제어할 수 있습니다.
예를 들어 fbcon=map:10은 VT0→fb1, VT1→fb0으로 매핑합니다.
DRM 사용 시 fbcon=off로 fbcon을 비활성화하고 DRM 콘솔(drm_fbdev_generic_setup())만 사용하는 것이 일반적입니다.
부팅 로고 렌더링
커널 부팅 시 좌측 상단에 표시되는 리눅스 Tux 로고는 fbcon이 렌더링합니다.
CONFIG_LOGO 옵션으로 활성화되며, CPU 코어 수만큼 로고가 나란히 표시됩니다.
/* 로고 관련 구조체 (include/linux/linux_logo.h) */
struct linux_logo {
int type; /* LINUX_LOGO_MONO, _VGA16, _CLUT224 */
unsigned int width;
unsigned int height;
unsigned int clutsize; /* CLUT224: 224개 색상 팔레트 */
const unsigned char *clut;
const unsigned char *data;
};
/* fbcon이 로고를 렌더링하는 흐름:
* fbcon_init() → fb_prepare_logo() → fb_show_logo()
* → fb_show_logo_line() — 로고 이미지를 fb_image로 변환
* → fb_imageblit() — VRAM에 전송
*
* 각 CPU당 로고 한 개씩 수평으로 배치
* 로고 영역 아래부터 콘솔 텍스트 시작 */
drivers/video/logo/에 PPM 형식의 로고 이미지를 추가하고
Kconfig에 등록하면 커스텀 부팅 로고를 사용할 수 있습니다.
CONFIG_LOGO_LINUX_CLUT224는 기본 224색 Tux 로고이며,
logo_linux_clut224.ppm을 교체하여 변경합니다.
fbcon=logo-pos:center 부트 파라미터로 로고 위치를 중앙에 배치할 수도 있습니다.
커서 처리
fbcon은 텍스트 커서를 두 가지 방식으로 렌더링합니다:
| 방식 | 구현 | 장점 | 단점 |
|---|---|---|---|
| 소프트웨어 커서 | fb_fillrect(ROP_XOR)으로 커서 위치 반전 |
모든 fbdev에서 동작 | 깜빡임 시 CPU 부하, tearing 가능 |
| 하드웨어 커서 | fb_cursor() 콜백으로 HW 커서 제어 |
CPU 부하 없음, 부드러운 깜빡임 | 드라이버 구현 필요, 크기 제한 |
/* fb_cursor 콜백 (하드웨어 커서 지원 시) */
static int myfb_cursor(struct fb_info *info,
struct fb_cursor *cursor)
{
struct myfb_priv *priv = info->par;
if (cursor->set & FB_CUR_SETPOS) {
/* 커서 위치 설정 */
writel(cursor->image.dx, priv->mmio + HW_CURSOR_X);
writel(cursor->image.dy, priv->mmio + HW_CURSOR_Y);
}
if (cursor->set & FB_CUR_SETIMAGE) {
/* 커서 이미지 설정 (최대 64×64 등 HW 제한) */
memcpy_toio(priv->mmio + HW_CURSOR_DATA,
cursor->image.data,
cursor->image.width * cursor->image.height / 8);
}
if (cursor->enable)
writel(CURSOR_EN, priv->mmio + HW_CURSOR_CTRL);
else
writel(0, priv->mmio + HW_CURSOR_CTRL);
return 0;
}
/* 하드웨어 커서를 지원하지 않으면 fb_cursor를 NULL로 두면
* fbcon이 자동으로 소프트웨어 커서(ROP_XOR fillrect)를 사용합니다 */
VT 전환과 fbcon
사용자가 Ctrl+Alt+F1~F6으로 VT를 전환하면 fbcon이 해당 VT의 화면 내용을 복원합니다:
fbcon_switch()가 호출되어 새 VT에 대한 fb_info를 선택합니다.fb_set_var()로 해당 VT의 해상도/모드를 복원합니다.fbcon_redraw()가 전체 화면을 다시 그립니다.- X11/Wayland가 활성 VT를 소유하면
KD_GRAPHICS모드가 설정되어 fbcon 출력이 비활성화됩니다.
KD_GRAPHICS 모드이며,
fbcon은 그 VT에 텍스트를 그리지 않습니다. Ctrl+Alt+F2 등으로 텍스트 콘솔 VT로 전환하면 KD_TEXT 모드의
VT에서 fbcon이 다시 활성화됩니다. chvt 2 명령으로도 VT를 전환할 수 있습니다.
Deferred I/O
fb_deferred_io는 시스템 메모리에 할당된 프레임버퍼의 변경 사항을 추적하여,
일정 시간 후 하드웨어에 일괄 전송하는 메커니즘입니다. SPI/I2C 연결 소형 디스플레이나 E-Ink 패널처럼
즉시 업데이트가 불가능하거나 비효율적인 디바이스에 특히 유용합니다.
fb_deferred_io 구조체
fb_deferred_io는 지연 시간과 콜백을 정의합니다:
변경 추적 원리
- 드라이버가
vmalloc()으로 시스템 메모리 프레임버퍼를 할당합니다. fb_deferred_io_init()호출 시, mmap의page_mkwrite핸들러가 설정됩니다.- 사용자가 mmap된 메모리에 쓰면 page fault가 발생하고, 해당 페이지가 dirty list에 추가됩니다.
- 설정된
delay시간 후delayed_work가 실행되어 드라이버의deferred_io콜백을 호출합니다. - 콜백에서 dirty 페이지 목록을 받아 실제 하드웨어에 전송합니다.
Deferred I/O 드라이버 예제
/* SPI 디스플레이 Deferred I/O 예제 */
static void myfb_deferred_io(struct fb_info *info,
struct list_head *pagereflist)
{
struct myfb_priv *priv = info->par;
struct fb_deferred_io_pageref *pageref;
unsigned long min_off = ULONG_MAX, max_off = 0;
/* dirty 영역의 최소/최대 오프셋 계산 */
list_for_each_entry(pageref, pagereflist, list) {
unsigned long off = pageref->offset;
if (off < min_off) min_off = off;
if (off + PAGE_SIZE > max_off) max_off = off + PAGE_SIZE;
}
/* dirty 영역만 SPI로 전송 */
myfb_spi_transfer(priv,
info->screen_buffer + min_off,
min_off, max_off - min_off);
}
static struct fb_deferred_io myfb_defio = {
.delay = HZ / 30, /* 약 33ms (30fps) */
.sort_pagereflist = true, /* 오프셋 순 정렬 */
.deferred_io = myfb_deferred_io,
};
/* probe에서 설정 */
info->fbdefio = &myfb_defio;
fb_deferred_io_init(info);
delay를 길게 설정(예: HZ = 1초)하면 여러 쓰기를 모아 한 번에 갱신하여 효율적입니다.
drivers/video/fbdev/broadsheet.c와 metronomefb.c가 E-Ink Deferred I/O의 실제 사례입니다.
Deferred I/O 내부 동작
fb_deferred_io 프레임워크의 핵심 구조체와 초기화/정리 순서를 살펴봅니다:
/* fb_deferred_io 구조체 (include/linux/fb.h) */
struct fb_deferred_io {
unsigned long delay; /* 워크큐 지연 시간 (jiffies) */
bool sort_pagereflist; /* dirty 리스트 오프셋 순 정렬 여부 */
int open_count; /* 열린 fd 수 (내부 관리) */
struct mutex lock; /* dirty 리스트 보호 뮤텍스 */
struct list_head pagereflist; /* dirty 페이지 리스트 */
struct delayed_work deferred_work; /* 워크큐 아이템 */
/* 드라이버 콜백 — dirty 페이지를 HW에 전송 */
void (*deferred_io)(struct fb_info *info,
struct list_head *pagereflist);
};
/* fb_deferred_io_pageref — dirty 페이지 정보 */
struct fb_deferred_io_pageref {
struct page *page; /* 변경된 페이지 */
unsigned long offset; /* 프레임버퍼 내 오프셋 */
struct list_head list; /* 연결 리스트 */
};
/* Deferred I/O 초기화 / 정리 순서 */
/* probe() 또는 fb_open() 시 */
static int myfb_init_defio(struct fb_info *info)
{
/* 1. 시스템 메모리로 프레임버퍼 할당 (vmalloc 필수!) */
info->screen_buffer = vzalloc(info->fix.smem_len);
if (!info->screen_buffer)
return -ENOMEM;
/* 2. fb_deferred_io 구조체 설정 */
info->fbdefio = &myfb_defio; /* .delay, .deferred_io 설정 */
/* 3. Deferred I/O 초기화 — vm_ops 교체, 워크큐 설정 */
fb_deferred_io_init(info);
return 0;
}
/* remove() 또는 fb_release() 시 */
static void myfb_cleanup_defio(struct fb_info *info)
{
/* 반드시 unregister_framebuffer 전에 호출! */
fb_deferred_io_cleanup(info);
vfree(info->screen_buffer);
info->screen_buffer = NULL;
}
/* 중요: Deferred I/O는 vmalloc() 메모리에서만 동작합니다.
* ioremap()이나 dma_alloc_coherent() 메모리에서는 사용할 수 없습니다.
* page fault 기반 추적이 vmalloc 페이지에만 적용되기 때문입니다. */
fb_deferred_io는 vmalloc()으로 할당된 메모리에서만 동작합니다.
ioremap()이나 dma_alloc_coherent() 메모리의 페이지는 VM 서브시스템의 page fault 추적 대상이 아니므로,
Deferred I/O의 page_mkwrite 핸들러가 호출되지 않습니다.
SPI/I2C/USB 디스플레이 드라이버에서는 반드시 vmalloc()으로 섀도 버퍼를 할당하고,
Deferred I/O 콜백에서 이 버퍼의 변경분을 실제 하드웨어로 전송해야 합니다.
부트 타임 프레임버퍼
부트 타임 프레임버퍼는 실제 GPU/디스플레이 드라이버가 로드되기 전에 화면 출력을 제공하는 간단한 드라이버들입니다. 펌웨어(BIOS/EFI)가 설정한 프레임버퍼를 그대로 사용하며, 모드 변경이나 하드웨어 가속 없이 픽셀만 출력합니다.
efifb — EFI 프레임버퍼
efifb는 EFI(UEFI) 펌웨어의 GOP(Graphics Output Protocol)가 설정한 프레임버퍼를 사용합니다.
커널 부트 시 EFI 부트 서비스에서 전달받은 프레임버퍼 주소와 해상도 정보를 그대로 사용하며,
모드 변경 기능은 없습니다. CONFIG_FB_EFI로 활성화합니다.
vesafb — VESA 프레임버퍼
vesafb는 BIOS의 VESA VBE(VESA BIOS Extensions) 인터페이스로 설정된 프레임버퍼를 사용합니다.
부트로더(GRUB 등)에서 vga= 파라미터로 모드를 선택하고, 커널은 그 모드를 유지합니다.
레거시 BIOS 부팅 시 사용되며, UEFI 시스템에서는 efifb를 사용합니다.
simplefb — 범용 심플 프레임버퍼
/* simplefb Device Tree 바인딩 예제 */
/* drivers/video/fbdev/simplefb.c 는 DT/플랫폼 디바이스로 등록 */
/* Device Tree 노드:
* framebuffer {
* compatible = "simple-framebuffer";
* reg = <0x80000000 (1920*1080*4)>;
* width = <1920>;
* height = <1080>;
* stride = <(1920*4)>;
* format = "a8r8g8b8";
* };
*/
/* simplefb는 부트로더가 사전 설정한 프레임버퍼를 커널에서 사용.
* 모드 변경 불가 — 부트로더가 설정한 해상도/포맷 그대로 유지.
* ARM/ARM64 임베디드에서 DRM 드라이버 로드 전 콘솔 출력에 활용. */
부트 프레임버퍼 핸드오프
실제 GPU 드라이버(DRM 또는 네이티브 fbdev)가 로드되면, 부트 프레임버퍼를 제거해야 합니다:
remove_conflicting_framebuffers()— 지정된 메모리 영역과 충돌하는 fbdev 제거remove_conflicting_pci_framebuffers()— PCI 디바이스와 충돌하는 fbdev 제거- DRM의
drm_aperture_remove_framebuffers()— DRM 드라이버에서 호출
fb_check_var에서 다른 해상도 요청을 거부합니다. 해상도를 변경하려면 실제 드라이버가 필요합니다.
simpledrm(drivers/gpu/drm/tiny/simpledrm.c)은 simplefb의 DRM 대응 버전으로, DRM 프레임워크에서 같은 역할을 합니다.
부트 프레임버퍼 커널 파라미터
부트 프레임버퍼의 동작을 제어하는 주요 커널 명령줄 파라미터들입니다:
| 파라미터 | 적용 대상 | 설명 |
|---|---|---|
video=efifb | efifb | efifb 활성화 (보통 자동 감지) |
video=efifb:off | efifb | efifb 비활성화 |
video=vesafb:mtrr:3 | vesafb | MTRR 설정 (0=없음, 3=write-combining) |
video=vesafb:ywrap | vesafb | Y-wrap 스크롤 활성화 |
vga=0x317 | vesafb | VESA 모드 번호 (1024×768 16bpp) |
vga=ask | vesafb | 부팅 시 모드 선택 메뉴 |
video=simplefb | simplefb | simplefb 활성화 |
video=simpledrm | simpledrm | simplefb 대신 simpledrm 사용 |
nomodeset | 전체 | KMS 모드 설정 비활성화 (부트 FB 유지) |
# GRUB에서 부트 프레임버퍼 설정 예시
# /etc/default/grub
# VESA 모드 지정 (레거시 BIOS)
GRUB_CMDLINE_LINUX="vga=0x31B" # 1280×1024 24bpp
# EFI 프레임버퍼 + fbcon 활성화
GRUB_CMDLINE_LINUX="fbcon=font:VGA8x16"
# GPU 드라이버 문제 시 부트 FB로 폴백
GRUB_CMDLINE_LINUX="nomodeset" # KMS 비활성화 → efifb/vesafb 유지
# 해상도 목록 확인 (VESA 모드)
# vga=ask로 부팅하면 사용 가능한 모드 번호가 표시됨
efifb 내부 동작
efifb가 커널에서 초기화되는 과정을 단계별로 살펴봅니다:
- 부트로더(GRUB EFI)가 EFI GOP(Graphics Output Protocol)를 호출하여 프레임버퍼를 설정합니다.
- 커널 EFI 스텁(
drivers/firmware/efi/)이screen_info구조체에 프레임버퍼 정보를 저장합니다. efifb_probe()가screen_info에서 주소, 해상도, 색상 깊이를 읽습니다.ioremap_wc()로 EFI가 설정한 프레임버퍼 물리 주소를 Write-Combining으로 매핑합니다.register_framebuffer()로 등록하면 fbcon이 즉시 콘솔을 표시합니다.
/* efifb 핵심 코드 (drivers/video/fbdev/efifb.c, 간략화) */
static int efifb_probe(struct platform_device *dev)
{
/* screen_info는 부팅 시 EFI 스텁이 채운 전역 구조체 */
info->var.xres = screen_info.lfb_width;
info->var.yres = screen_info.lfb_height;
info->var.bits_per_pixel = screen_info.lfb_depth;
info->fix.smem_start = screen_info.lfb_base;
info->fix.smem_len = screen_info.lfb_size;
info->fix.line_length = screen_info.lfb_linelength;
/* WC(Write-Combining)로 VRAM 매핑 */
info->screen_base = ioremap_wc(info->fix.smem_start,
info->fix.smem_len);
/* 색상 필드는 screen_info에서 직접 가져옴 */
info->var.red.offset = screen_info.red_pos;
info->var.red.length = screen_info.red_size;
/* green, blue도 동일 ... */
/* fb_ops는 최소한의 것만: cfb_fillrect, cfb_copyarea, cfb_imageblit
* fb_check_var, fb_set_par는 해상도 변경을 거부 */
return register_framebuffer(info);
}
efifb 대신 simpledrm을 기본으로 사용하는 추세입니다.
simpledrm은 DRM 프레임워크를 사용하므로 Wayland 등 현대 디스플레이 서버와의 호환성이 더 좋습니다.
CONFIG_DRM_SIMPLEDRM=y가 설정되면 efifb 대신 simpledrm이 부트 디스플레이를 담당합니다.
사용자 공간 인터페이스
사용자 공간에서 프레임버퍼에 접근하는 주요 방법은 /dev/fbN 디바이스 파일을 통한
ioctl()과 mmap()입니다.
주요 ioctl 명령
| ioctl | 방향 | 설명 |
|---|---|---|
FBIOGET_VSCREENINFO | GET | 가변 화면 정보 읽기 (해상도, 색상) |
FBIOPUT_VSCREENINFO | SET | 가변 화면 정보 설정 (모드 변경) |
FBIOGET_FSCREENINFO | GET | 고정 화면 정보 읽기 (메모리 주소, 크기) |
FBIOPAN_DISPLAY | SET | 가시 영역 이동 (더블 버퍼 플립) |
FBIO_WAITFORVSYNC | — | 수직 동기 대기 |
FBIOGETCMAP | GET | 색상 맵 읽기 |
FBIOPUTCMAP | SET | 색상 맵 설정 |
FBIOBLANK | SET | 화면 끄기/켜기 (DPMS) |
FBIO_CURSOR | SET | 하드웨어 커서 설정 |
sysfs 속성
/sys/class/graphics/fb0/에서 다양한 속성을 확인할 수 있습니다:
name— 드라이버 이름bits_per_pixel— 픽셀당 비트 수virtual_size— 가상 해상도stride— 라인 바이트 길이blank— 블랭킹 상태modes— 지원되는 모드 목록
사용자 공간 도구
# fbset — 프레임버퍼 설정 도구
fbset -fb /dev/fb0 -i # 현재 설정 정보 출력
fbset -fb /dev/fb0 -g 1920 1080 1920 2160 32 # 해상도/깊이 변경
fbset -fb /dev/fb0 -depth 16 # 색상 깊이 변경
fbset -fb /dev/fb0 -accel true # 가속 활성화
# 기타 도구
cat /proc/fb # 등록된 프레임버퍼 목록
cat /dev/fb0 > screenshot.raw # 화면 캡처 (raw)
fbi -d /dev/fb0 image.png # 이미지 표시 (fbi 도구)
dd if=/dev/zero of=/dev/fb0 # 화면 지우기 (검은색)
mmap으로 직접 그리기
/* 사용자 공간에서 프레임버퍼에 직접 그리기 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
int main(void)
{
int fd = open("/dev/fb0", O_RDWR);
if (fd < 0) { perror("open"); return 1; }
/* 화면 정보 읽기 */
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
ioctl(fd, FBIOGET_VSCREENINFO, &var);
ioctl(fd, FBIOGET_FSCREENINFO, &fix);
printf("해상도: %ux%u, %ubpp, stride=%u\n",
var.xres, var.yres, var.bits_per_pixel, fix.line_length);
/* 프레임버퍼 mmap */
size_t screensize = fix.smem_len;
char *fbp = mmap(NULL, screensize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (fbp == MAP_FAILED) { perror("mmap"); close(fd); return 1; }
/* 화면을 빨간색으로 채우기 (XRGB8888 가정) */
if (var.bits_per_pixel == 32) {
for (unsigned y = 0; y < var.yres; y++) {
unsigned *line = (unsigned *)(fbp + y * fix.line_length);
for (unsigned x = 0; x < var.xres; x++) {
/* XRGB8888: 0x00RRGGBB */
line[x] = 0x00FF0000; /* 빨강 */
}
}
}
/* 중앙에 100x100 파란 사각형 그리기 */
if (var.bits_per_pixel == 32) {
unsigned cx = var.xres / 2 - 50;
unsigned cy = var.yres / 2 - 50;
for (unsigned y = cy; y < cy + 100 && y < var.yres; y++) {
unsigned *line = (unsigned *)(fbp + y * fix.line_length);
for (unsigned x = cx; x < cx + 100 && x < var.xres; x++) {
line[x] = 0x000000FF; /* 파랑 */
}
}
}
munmap(fbp, screensize);
close(fd);
return 0;
}
화면 캡처와 복원
/* 프레임버퍼 스크린샷 캡처 유틸리티 */
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
/* PPM(P6) 형식으로 스크린샷 저장 */
static int fb_screenshot_ppm(const char *fbdev, const char *output)
{
int fd = open(fbdev, O_RDONLY);
struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
FILE *fp;
unsigned int x, y;
ioctl(fd, FBIOGET_VSCREENINFO, &var);
ioctl(fd, FBIOGET_FSCREENINFO, &fix);
unsigned char *fb = mmap(NULL, fix.smem_len,
PROT_READ, MAP_SHARED, fd, 0);
fp = fopen(output, "wb");
fprintf(fp, "P6\n%u %u\n255\n", var.xres, var.yres);
for (y = 0; y < var.yres; y++) {
for (x = 0; x < var.xres; x++) {
unsigned long off = y * fix.line_length +
x * (var.bits_per_pixel / 8);
if (var.bits_per_pixel == 32) {
/* XRGB8888 → RGB */
unsigned int pixel = *(unsigned int *)(fb + off);
unsigned char rgb[3] = {
(pixel >> var.red.offset) & 0xFF,
(pixel >> var.green.offset) & 0xFF,
(pixel >> var.blue.offset) & 0xFF
};
fwrite(rgb, 1, 3, fp);
}
}
}
fclose(fp);
munmap(fb, fix.smem_len);
close(fd);
return 0;
}
-ENOTTY를 반환합니다.
FBIOPUT_VSCREENINFO가 -EINVAL을 반환하면 요청한 해상도/포맷이 드라이버의 fb_check_var에서 거부된 것입니다.
-ENOSYS는 해당 기능이 구현되지 않았음을 의미합니다.
fbdev vs DRM/KMS 비교
fbdev와 DRM/KMS는 모두 디스플레이 하드웨어에 접근하는 커널 서브시스템이지만, 설계 철학과 기능 범위가 크게 다릅니다.
아키텍처 차이
- fbdev: 단일
fb_info구조체 중심. 디스플레이 하드웨어를 단순 메모리 버퍼로 추상화. CRTC·Encoder·Connector 같은 객체 모델이 없음. - DRM/KMS: 디스플레이 파이프라인을 CRTC, Plane, Encoder, Connector 객체로 세분화. Atomic commit으로 원자적 상태 전환. GEM/TTM으로 GPU 메모리 관리.
기능 비교
| 기능 | fbdev | DRM/KMS |
|---|---|---|
| 모드 설정 | 단일 ioctl (FBIOPUT_VSCREENINFO) | Atomic modesetting (원자적) |
| 다중 디스플레이 | 제한적 (fb0, fb1 독립) | 완전 지원 (CRTC 연결) |
| 하드웨어 가속 | 2D blitter만 (fillrect 등) | 3D, 비디오 디코딩, 컴퓨트 |
| 오버레이 플레인 | 미지원 | 완전 지원 (primary, overlay, cursor) |
| Vsync | FBIO_WAITFORVSYNC (제한적) | page flip + vblank 이벤트 |
| 메모리 관리 | 단순 mmap (드라이버 직접) | GEM/TTM (참조 카운팅, 공유) |
| 버퍼 공유 | 미지원 | DMA-BUF (GPU↔카메라↔디스플레이) |
| 전원 관리 | FBIOBLANK만 | DPMS + 런타임 PM |
| 사용자 공간 | fbset, fbi | Mesa, Wayland, libdrm |
| 커널 콘솔 | fbcon (네이티브) | drm_fbdev_generic (에뮬레이션) |
fbdev → DRM 마이그레이션
기존 fbdev 드라이버를 DRM으로 전환하는 일반적인 경로:
drm_simple_display_pipe— 가장 간단한 DRM 드라이버 형태 (단일 CRTC, 단일 Plane, 단일 Connector)drm_gem_dma_helper— DMA coherent 메모리 기반 GEM 객체 관리drm_fbdev_generic_setup()— DRM 드라이버에서 fbdev 호환 계층 제공drivers/gpu/drm/tiny/— SPI/I2C 소형 디스플레이 DRM 드라이버 참고 (ili9341, st7789, ssd130x 등)
DRM fbdev 호환 계층
DRM 드라이버가 drm_fbdev_generic_setup()을 호출하면, DRM 위에 fbdev 에뮬레이션 계층이 생성되어
/dev/fb0를 통한 레거시 접근이 가능합니다. fbcon도 이 계층을 통해 동작합니다.
자세한 내용은 GPU 서브시스템 — fbdev 에뮬레이션을 참고하세요.
fbdev → DRM 코드 비교
같은 기능(프레임버퍼 등록)을 fbdev와 DRM에서 각각 어떻게 구현하는지 비교합니다:
/* ============ fbdev 방식 ============ */
/* 단순, 직관적, 제한적 */
static int fbdev_init(struct platform_device *pdev)
{
struct fb_info *info;
info = framebuffer_alloc(sizeof(*priv), &pdev->dev);
/* fb_fix, fb_var, fb_ops 직접 설정 ... */
info->fbops = &myfb_ops;
info->screen_base = vram;
return register_framebuffer(info);
/* → /dev/fb0 생성, fbcon 자동 연결 */
}
/* ============ DRM 방식 ============ */
/* 구조화, 확장 가능, 복잡 */
static int drm_init(struct platform_device *pdev)
{
struct drm_device *drm;
drm = drm_dev_alloc(&my_driver, &pdev->dev);
/* DRM 오브젝트 파이프라인 구성 */
drm_simple_display_pipe_init(drm, &pipe,
&pipe_funcs, /* CRTC/Plane 콜백 */
formats, n_formats, /* 지원 포맷 목록 */
NULL, /* 모디파이어 */
&connector); /* 커넥터 */
drm_mode_config_reset(drm);
drm_dev_register(drm, 0);
/* fbdev 에뮬레이션 (레거시 호환) */
drm_fbdev_generic_setup(drm, 32);
/* → /dev/dri/card0 + /dev/fb0 생성 */
return 0;
}
drivers/gpu/drm/tiny/ 디렉토리의 드라이버들은 fbdev에서 DRM으로 전환하는
최소 예제입니다. ili9341.c(SPI LCD), ssd130x.c(OLED), st7789.c(TFT LCD) 등이 있으며,
drm_simple_display_pipe을 사용하여 fbdev와 비슷한 수준의 단순함으로 DRM 드라이버를 구현합니다.
새 디스플레이 드라이버를 작성한다면 fbdev 대신 DRM tiny 스타일을 권장합니다.
드라이버 작성 가이드
최소한의 fbdev 플랫폼 드라이버를 처음부터 작성하는 방법을 단계별로 안내합니다. 이 드라이버는 DMA coherent 메모리를 사용하는 임베디드 LCD 컨트롤러를 가정합니다.
최소 드라이버 골격
/* 최소 fbdev 플랫폼 드라이버 — myfb.c */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/fb.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
struct myfb_priv {
void __iomem *mmio; /* LCD 컨트롤러 레지스터 */
void *vram_virt; /* DMA 가상 주소 */
dma_addr_t vram_phys; /* DMA 물리 주소 */
size_t vram_size; /* VRAM 크기 */
struct fb_info *info;
u32 pseudo_palette[16]; /* fbcon용 팔레트 */
};
/* --- fb_check_var: 파라미터 검증 (HW 변경 금지) --- */
static int myfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
/* 32bpp XRGB8888만 지원 */
if (var->bits_per_pixel != 32)
var->bits_per_pixel = 32;
var->red.offset = 16; var->red.length = 8;
var->green.offset = 8; var->green.length = 8;
var->blue.offset = 0; var->blue.length = 8;
var->transp.offset = 0; var->transp.length = 0;
/* 가상 해상도: 더블 버퍼 허용 */
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
if (var->yres_virtual > var->yres * 2)
var->yres_virtual = var->yres * 2;
return 0;
}
/* --- fb_set_par: 실제 하드웨어 모드 설정 --- */
static int myfb_set_par(struct fb_info *info)
{
struct myfb_priv *priv = info->par;
/* LCD 컨트롤러에 해상도/타이밍 설정 */
writel(info->var.xres, priv->mmio + LCD_HRES);
writel(info->var.yres, priv->mmio + LCD_VRES);
writel(info->var.bits_per_pixel, priv->mmio + LCD_BPP);
/* line_length 재계산 */
info->fix.line_length = info->var.xres *
(info->var.bits_per_pixel / 8);
/* LCD 활성화 */
writel(LCD_EN | LCD_TFT, priv->mmio + LCD_CTRL);
return 0;
}
/* --- fb_setcolreg: pseudo_palette 설정 --- */
static int myfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
u32 *pal = info->pseudo_palette;
if (regno >= 16)
return -EINVAL;
red >>= (16 - info->var.red.length);
green >>= (16 - info->var.green.length);
blue >>= (16 - info->var.blue.length);
pal[regno] = (red << info->var.red.offset) |
(green << info->var.green.offset) |
(blue << info->var.blue.offset);
return 0;
}
/* --- fb_blank: DPMS 제어 --- */
static int myfb_blank(int blank, struct fb_info *info)
{
struct myfb_priv *priv = info->par;
switch (blank) {
case FB_BLANK_UNBLANK:
writel(readl(priv->mmio + LCD_CTRL) | LCD_EN,
priv->mmio + LCD_CTRL);
break;
case FB_BLANK_POWERDOWN:
writel(readl(priv->mmio + LCD_CTRL) & ~LCD_EN,
priv->mmio + LCD_CTRL);
break;
default:
break;
}
return 0;
}
/* --- fb_pan_display: 더블 버퍼 플립 --- */
static int myfb_pan_display(struct fb_var_screeninfo *var,
struct fb_info *info)
{
struct myfb_priv *priv = info->par;
unsigned long offset;
if (var->yoffset + info->var.yres > info->var.yres_virtual)
return -EINVAL;
offset = var->yoffset * info->fix.line_length;
writel(priv->vram_phys + offset, priv->mmio + LCD_BASE_ADDR);
return 0;
}
/* --- fb_ops 정의 --- */
static const struct fb_ops myfb_ops = {
.owner = THIS_MODULE,
.fb_check_var = myfb_check_var,
.fb_set_par = myfb_set_par,
.fb_setcolreg = myfb_setcolreg,
.fb_blank = myfb_blank,
.fb_pan_display = myfb_pan_display,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
/* --- probe: 디바이스 초기화 + 등록 --- */
static int myfb_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct myfb_priv *priv;
struct fb_info *info;
struct resource *res;
int ret;
/* fb_info + priv 할당 */
info = framebuffer_alloc(sizeof(*priv), dev);
if (!info)
return -ENOMEM;
priv = info->par;
priv->info = info;
/* MMIO 레지스터 매핑 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->mmio)) {
ret = PTR_ERR(priv->mmio);
goto err_release;
}
/* DMA 프레임버퍼 할당 (1920×1080×4 × 2 = 더블 버퍼) */
priv->vram_size = 1920 * 1080 * 4 * 2;
priv->vram_virt = dma_alloc_coherent(dev, priv->vram_size,
&priv->vram_phys, GFP_KERNEL);
if (!priv->vram_virt) {
ret = -ENOMEM;
goto err_release;
}
/* fb_fix_screeninfo 설정 */
strscpy(info->fix.id, "myfb", sizeof(info->fix.id));
info->fix.type = FB_TYPE_PACKED_PIXELS;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.accel = FB_ACCEL_NONE;
info->fix.smem_start = priv->vram_phys;
info->fix.smem_len = priv->vram_size;
info->fix.line_length = 1920 * 4;
info->fix.ypanstep = 1; /* 팬 디스플레이 허용 */
/* fb_var_screeninfo 설정 */
info->var.xres = 1920;
info->var.yres = 1080;
info->var.xres_virtual = 1920;
info->var.yres_virtual = 2160; /* 더블 버퍼 */
info->var.bits_per_pixel = 32;
info->var.red.offset = 16; info->var.red.length = 8;
info->var.green.offset = 8; info->var.green.length = 8;
info->var.blue.offset = 0; info->var.blue.length = 8;
info->var.activate = FB_ACTIVATE_NOW;
/* 연결 */
info->fbops = &myfb_ops;
info->screen_base = priv->vram_virt;
info->screen_size = priv->vram_size;
info->pseudo_palette = priv->pseudo_palette;
info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
/* 등록 → /dev/fbN 생성, fbcon 자동 연결 */
ret = register_framebuffer(info);
if (ret)
goto err_dma;
platform_set_drvdata(pdev, priv);
dev_info(dev, "myfb: %ux%u %ubpp @ 0x%llx\n",
info->var.xres, info->var.yres,
info->var.bits_per_pixel,
(unsigned long long)priv->vram_phys);
return 0;
err_dma:
dma_free_coherent(dev, priv->vram_size,
priv->vram_virt, priv->vram_phys);
err_release:
framebuffer_release(info);
return ret;
}
/* --- remove: 정리 --- */
static void myfb_remove(struct platform_device *pdev)
{
struct myfb_priv *priv = platform_get_drvdata(pdev);
struct device *dev = &pdev->dev;
unregister_framebuffer(priv->info);
dma_free_coherent(dev, priv->vram_size,
priv->vram_virt, priv->vram_phys);
framebuffer_release(priv->info);
}
static const struct of_device_id myfb_of_match[] = {
{ .compatible = "vendor,my-lcd-controller" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, myfb_of_match);
static struct platform_driver myfb_driver = {
.probe = myfb_probe,
.remove = myfb_remove,
.driver = {
.name = "myfb",
.of_match_table = myfb_of_match,
},
};
module_platform_driver(myfb_driver);
MODULE_DESCRIPTION("Minimal fbdev platform driver example");
MODULE_LICENSE("GPL");
플랫폼 통합
실제 하드웨어에서는 다음 리소스를 플랫폼 디바이스 또는 Device Tree에서 가져옵니다:
- MMIO 레지스터 —
platform_get_resource(pdev, IORESOURCE_MEM, 0) - IRQ —
platform_get_irq(pdev, 0)(vsync 인터럽트) - 클럭 —
devm_clk_get(dev, "pixel")(픽셀 클럭) - 리셋 —
devm_reset_control_get(dev, NULL)(LCD 리셋)
Device Tree 바인딩
/* Device Tree 바인딩 예제 */
lcd-controller@40020000 {
compatible = "vendor,my-lcd-controller";
reg = <0x40020000 0x1000>; /* MMIO 레지스터 */
interrupts = <GIC_SPI 88 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc LCD_CLK>;
clock-names = "pixel";
resets = <&rcc LCD_RST>;
/* 디스플레이 타이밍 */
display-timings {
native-mode = <&timing0>;
timing0: timing0 {
clock-frequency = <148500000>;
hactive = <1920>;
vactive = <1080>;
hfront-porch = <88>;
hback-porch = <148>;
hsync-len = <44>;
vfront-porch = <4>;
vback-porch = <36>;
vsync-len = <5>;
};
};
};
등록 API 요약
framebuffer_alloc(size, dev)—fb_info+ par 할당register_framebuffer(info)—/dev/fbN생성, sysfs 등록, fbcon 연결unregister_framebuffer(info)— fbcon 분리,/dev/fbN제거framebuffer_release(info)— 메모리 해제fb_set_var(info, &var)— 커널 내부에서 var 설정fb_pan_display(info, &var)— 커널 내부에서 팬 디스플레이
remove 함수에서 반드시 unregister_framebuffer() →
DMA 해제 → framebuffer_release() 순서를 지키세요. 순서가 잘못되면 use-after-free가 발생할 수 있습니다.
devm_* 계열 함수를 적극 활용하면 해제 순서 문제를 줄일 수 있습니다.
전원 관리 (Suspend/Resume)
fbdev 드라이버에서 시스템 절전(suspend/resume)을 지원하려면 플랫폼 드라이버의 PM 콜백을 구현합니다:
/* fbdev 드라이버의 suspend/resume 구현 */
static int myfb_suspend(struct device *dev)
{
struct myfb_priv *priv = dev_get_drvdata(dev);
struct fb_info *info = priv->info;
/* 1. fbcon에게 콘솔 출력 중단 알림 */
console_lock();
fb_set_suspend(info, 1); /* fbcon 일시정지 */
console_unlock();
/* 2. LCD 컨트롤러 비활성화 */
writel(0, priv->mmio + LCD_CTRL);
/* 3. 클럭 비활성화 */
clk_disable_unprepare(priv->pixel_clk);
return 0;
}
static int myfb_resume(struct device *dev)
{
struct myfb_priv *priv = dev_get_drvdata(dev);
struct fb_info *info = priv->info;
/* 1. 클럭 재활성화 */
clk_prepare_enable(priv->pixel_clk);
/* 2. LCD 컨트롤러 재초기화 */
myfb_set_par(info); /* 해상도/타이밍 재설정 */
/* 3. fbcon 재개 */
console_lock();
fb_set_suspend(info, 0); /* fbcon 재개 → 화면 복원 */
console_unlock();
return 0;
}
static DEFINE_SIMPLE_DEV_PM_OPS(myfb_pm_ops,
myfb_suspend, myfb_resume);
static struct platform_driver myfb_driver = {
.driver = {
.name = "myfb",
.pm = pm_sleep_ptr(&myfb_pm_ops),
},
};
fb_set_suspend(info, 1) 호출 시 fbcon은 해당 프레임버퍼에 대한 모든 출력을 중단합니다.
Resume 시 fb_set_suspend(info, 0)을 호출하면 fbcon이 화면을 다시 그리며,
FB_EVENT_RESUME 알림도 발생합니다.
반드시 console_lock()/console_unlock() 사이에서 호출해야 합니다.
일반적인 에러 처리 패턴
fbdev 드라이버에서 흔히 사용되는 에러 처리 패턴들입니다:
/* 패턴 1: goto 체인 에러 처리 (전통적) */
static int myfb_probe(struct platform_device *pdev)
{
struct fb_info *info;
int ret;
info = framebuffer_alloc(sizeof(*priv), &pdev->dev);
if (!info)
return -ENOMEM;
priv->clk = devm_clk_get(&pdev->dev, "pixel");
if (IS_ERR(priv->clk)) {
ret = PTR_ERR(priv->clk);
goto err_release_fb;
}
ret = clk_prepare_enable(priv->clk);
if (ret)
goto err_release_fb;
/* DMA 할당 ... */
if (!priv->vram_virt) {
ret = -ENOMEM;
goto err_clk;
}
ret = register_framebuffer(info);
if (ret)
goto err_dma;
return 0; /* 성공 */
err_dma:
dma_free_coherent(&pdev->dev, ...);
err_clk:
clk_disable_unprepare(priv->clk);
err_release_fb:
framebuffer_release(info);
return ret;
}
/* 패턴 2: devm_* 활용 (권장 — 자동 해제) */
static int myfb_probe_devm(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fb_info *info;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
/* devm_clk_get: 디바이스 제거 시 자동 put */
priv->clk = devm_clk_get_enabled(dev, "pixel");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
/* devm_ioremap_resource: 자동 unmap */
priv->mmio = devm_ioremap_resource(dev, res);
if (IS_ERR(priv->mmio))
return PTR_ERR(priv->mmio);
/* 주의: framebuffer_alloc/register_framebuffer는
* devm 버전이 없으므로 수동 해제 필요 */
return register_framebuffer(info);
}
Virtual Framebuffer (vfb)
vfb(Virtual Framebuffer)는 실제 디스플레이 하드웨어 없이 동작하는 소프트웨어 프레임버퍼입니다.
시스템 메모리에 가상 화면 버퍼를 할당하며, fbdev API 테스트, 헤드리스 서버의 가상 디스플레이,
프레임버퍼 프로그래밍 학습 등에 활용됩니다. 소스는 drivers/video/fbdev/vfb.c에 있습니다.
vfb 모듈 설정
CONFIG_FB_VIRTUAL=m으로 커널 설정 후 모듈로 빌드합니다.
vfb 사용법
# vfb 모듈 로드 (1024×768, 32bpp 기본)
sudo modprobe vfb vfb_enable=1
# 등록 확인
cat /proc/fb
# 0 Virtual FB ← vfb가 fb0으로 등록됨
# fbset으로 확인
fbset -fb /dev/fb0 -i
# 가상 프레임버퍼에 그리기 테스트
dd if=/dev/urandom of=/dev/fb0 bs=1024 count=$((768*4))
# 모듈 제거
sudo modprobe -r vfb
vfb 내부 구현
vfb는 fbdev 학습에 최적의 참고 코드입니다. ~250줄의 매우 간결한 구현이며,
핵심 동작은 다음과 같습니다:
/* vfb 핵심 동작 (drivers/video/fbdev/vfb.c, 간략화) */
/* 1. vmalloc()으로 가상 VRAM 할당 */
static void *videomemory;
static u_long videomemorysize = 1024 * 768 * 4; /* 기본 크기 */
/* 2. 최소한의 fb_ops (sys_* 헬퍼 사용) */
static const struct fb_ops vfb_ops = {
.owner = THIS_MODULE,
.fb_read = fb_sys_read,
.fb_write = fb_sys_write,
.fb_check_var = vfb_check_var,
.fb_set_par = vfb_set_par,
.fb_setcolreg = vfb_setcolreg,
.fb_pan_display = vfb_pan_display,
.fb_fillrect = sys_fillrect, /* 시스템 메모리용 */
.fb_copyarea = sys_copyarea,
.fb_imageblit = sys_imageblit,
.fb_mmap = vfb_mmap,
};
/* 3. vfb_mmap — vmalloc 메모리를 유저에게 페이지 단위 매핑 */
static int vfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
{
return remap_vmalloc_range(vma, videomemory, vma->vm_pgoff);
/* remap_pfn_range가 아닌 remap_vmalloc_range 사용!
* vmalloc 메모리는 물리적으로 비연속이므로 전용 함수 필요 */
}
/* vfb는 Deferred I/O 없이 sys_* 헬퍼만 사용하므로,
* mmap 쓰기가 즉시 VRAM(vmalloc 버퍼)에 반영됩니다.
* 실제 디스플레이가 없으므로 변경 사항은 메모리에만 존재합니다. */
vfb를 사용하세요.
QEMU에서도 modprobe vfb로 가상 프레임버퍼를 만들고, /dev/fb0에 mmap으로 그리기 테스트를 할 수 있습니다.
cat /dev/fb0 > screenshot.raw로 가상 화면을 덤프한 후 호스트에서 raw 이미지 뷰어로 확인할 수 있습니다.
(ffplay -f rawvideo -pixel_format bgra -video_size 1024x768 screenshot.raw)
디버깅과 도구
fbdev 드라이버 개발 시 유용한 디버깅 기법과 도구를 소개합니다.
/proc/fb
/proc/fb는 현재 등록된 모든 프레임버퍼의 인덱스와 이름을 보여줍니다:
$ cat /proc/fb
0 EFI VGA
1 i915drmfb
ftrace / bpftrace
# fbdev 관련 ftrace 이벤트 추적
echo 1 > /sys/kernel/debug/tracing/events/fb/enable
cat /sys/kernel/debug/tracing/trace_pipe
# fb_ops 콜백 추적 (kprobe)
echo 'p:fb_open fb_open' >> /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/kprobes/fb_open/enable
# bpftrace로 fb_mmap 호출 추적
bpftrace -e 'kprobe:fb_mmap { printf("fb_mmap pid=%d\n", pid); }'
# ftrace로 fb_ioctl 추적 (어떤 ioctl이 호출되는지)
echo 'p:fb_ioctl fb_ioctl cmd=%si' >> /sys/kernel/debug/tracing/kprobe_events
echo 1 > /sys/kernel/debug/tracing/events/kprobes/fb_ioctl/enable
cat /sys/kernel/debug/tracing/trace_pipe
일반적인 문제와 해결
- 화면 검은색:
screen_base/fix.smem_start설정 확인, LCD 컨트롤러 활성화 여부, 클럭 설정 확인 - 색상 이상:
fb_var_screeninfo의 색상 필드(red/green/blue offset, length) 확인, 엔디안 설정 - 화면 깨짐:
fix.line_length(stride)가 실제 하드웨어와 일치하는지 확인 - mmap 실패:
fix.smem_start가 페이지 정렬되었는지,fix.smem_len이 충분한지 확인 - fbcon 미동작:
CONFIG_FRAMEBUFFER_CONSOLE=y,fbcon=off부트 파라미터가 없는지 확인
cat /proc/fb로 현재 어떤 프레임버퍼 드라이버가 등록되어 있는지 빠르게 확인하세요.
부트 프레임버퍼(efifb/vesafb)가 실제 드라이버로 올바르게 교체되었는지 확인하는 데 유용합니다.
debugfs 인터페이스
일부 fbdev 드라이버는 debugfs에 추가 정보를 제공합니다:
# debugfs 마운트 확인
mount | grep debugfs
# debugfs on /sys/kernel/debug type debugfs (rw,nosuid,nodev,noexec)
# fbdev 관련 debugfs (드라이버에 따라 다름)
ls /sys/kernel/debug/dri/0/ # DRM fbdev 에뮬레이션 시
# sysfs를 통한 상세 정보 확인
cat /sys/class/graphics/fb0/name # 드라이버 이름
cat /sys/class/graphics/fb0/stride # stride (line_length)
cat /sys/class/graphics/fb0/bits_per_pixel
cat /sys/class/graphics/fb0/virtual_size # xres_virtual,yres_virtual
cat /sys/class/graphics/fb0/pan # 현재 pan 오프셋
# 프레임버퍼 상태 종합 확인 스크립트
for attr in name stride bits_per_pixel virtual_size pan blank; do
echo -n "$attr: "
cat /sys/class/graphics/fb0/$attr 2>/dev/null || echo "N/A"
done
성능 측정
fbdev 드라이버의 그리기 성능을 측정하는 방법:
/* 커널 내 성능 측정 (드라이버 개발 시) */
#include <linux/ktime.h>
static void myfb_benchmark_fillrect(struct fb_info *info)
{
struct fb_fillrect rect = {
.dx = 0, .dy = 0,
.width = info->var.xres,
.height = info->var.yres,
.color = 0, .rop = ROP_COPY,
};
ktime_t start, end;
int i;
start = ktime_get();
for (i = 0; i < 100; i++)
info->fbops->fb_fillrect(info, &rect);
end = ktime_get();
pr_info("fillrect %ux%u × 100: %lld us\n",
rect.width, rect.height,
ktime_to_us(ktime_sub(end, start)));
}
# 유저스페이스에서 간단한 mmap 쓰기 벤치마크
time dd if=/dev/zero of=/dev/fb0 bs=1M count=8
# → 쓰기 대역폭 측정 (MB/s)
# fbtest 도구 (fbdev 벤치마크)
# https://github.com/nicupavel/fbtest
fbtest --device /dev/fb0 --test fill
fbtest --device /dev/fb0 --test scroll
커널 로그 활용
fbdev 관련 커널 로그 메시지를 필터링하여 문제를 진단합니다:
# fbdev 관련 dmesg 필터
dmesg | grep -iE 'fb[0-9]|framebuffer|fbcon|fbmem|efifb|vesafb|simplefb'
# 주요 로그 메시지 해석:
# "fb0: EFI VGA frame buffer device"
# → efifb가 fb0으로 등록됨
#
# "fbcon: Deferring console take-over"
# → fbcon이 콘솔 인수를 나중으로 연기
#
# "fb0: switching to i915drmfb from EFI VGA"
# → DRM 드라이버(i915)가 efifb를 교체
#
# "Console: switching to colour frame buffer device 240x67"
# → fbcon이 텍스트 모드에서 프레임버퍼 콘솔로 전환
# (240×67은 8×16 폰트 기준 문자 수)
# 동적 디버그 활성화 (더 자세한 로그)
echo 'module fb_sys_fops +p' > /sys/kernel/debug/dynamic_debug/control
echo 'file drivers/video/fbdev/core/fbmem.c +p' > /sys/kernel/debug/dynamic_debug/control
커널 설정 (Kconfig)
| 옵션 | 설명 | 권장 |
|---|---|---|
CONFIG_FB | 프레임버퍼 서브시스템 활성화 | Y (fbdev 사용 시) |
CONFIG_FRAMEBUFFER_CONSOLE | fbcon (프레임버퍼 콘솔) | Y (콘솔 필요 시) |
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY | 주 디스플레이 자동 감지 | Y |
CONFIG_FB_EFI | EFI 프레임버퍼 (efifb) | Y (UEFI 시스템) |
CONFIG_FB_VESA | VESA 프레임버퍼 (vesafb) | Y (BIOS 시스템) |
CONFIG_FB_SIMPLE | Simple 프레임버퍼 (simplefb) | Y (임베디드) |
CONFIG_FB_VIRTUAL | Virtual 프레임버퍼 (vfb) | M (테스트용) |
CONFIG_FB_CFB_FILLRECT | cfb_fillrect 헬퍼 | 자동 선택 |
CONFIG_FB_CFB_COPYAREA | cfb_copyarea 헬퍼 | 자동 선택 |
CONFIG_FB_CFB_IMAGEBLIT | cfb_imageblit 헬퍼 | 자동 선택 |
CONFIG_FB_SYS_FILLRECT | sys_fillrect 헬퍼 | 자동 선택 |
CONFIG_FB_SYS_COPYAREA | sys_copyarea 헬퍼 | 자동 선택 |
CONFIG_FB_SYS_IMAGEBLIT | sys_imageblit 헬퍼 | 자동 선택 |
CONFIG_FB_DEFERRED_IO | Deferred I/O 지원 | 자동 선택 |
CONFIG_FONT_8x8 | 8×8 콘솔 폰트 | 선택 |
CONFIG_FONT_8x16 | 8×16 콘솔 폰트 (기본) | Y |
CONFIG_LOGO | 부팅 시 리눅스 로고 표시 | 선택 |
용도별 설정 시나리오
| 시나리오 | 필수 옵션 | 권장 옵션 | 비활성화 |
|---|---|---|---|
| 임베디드 LCD (SoC 디스플레이) |
CONFIG_FB=yCONFIG_FB_CFB_*SoC 드라이버 |
FRAMEBUFFER_CONSOLEFB_DEFERRED_IOLOGO |
FB_EFI, FB_VESA |
| SPI/I2C 소형 디스플레이 (IoT, E-Ink) |
CONFIG_FB=yCONFIG_FB_SYS_*FB_DEFERRED_IO |
FRAMEBUFFER_CONSOLEFONT_8x8 (작은 화면) |
FB_CFB_*, FB_EFI |
| 데스크탑 (DRM 기반) (GPU 드라이버 사용) |
CONFIG_DRM=yGPU 드라이버 |
DRM_FBDEV_EMULATIONFB_EFI (부팅용) |
대부분의 FB_*(DRM이 대체) |
| 헤드리스 서버 (원격 관리) |
CONFIG_FB=mFB_VIRTUAL |
FB_EFI (BMC 콘솔) |
FRAMEBUFFER_CONSOLELOGO |
| fbdev 드라이버 개발 (테스트/학습) |
CONFIG_FB=yFB_VIRTUAL=mFB_CFB_*FB_SYS_* |
FRAMEBUFFER_CONSOLEFB_DEFERRED_IODYNAMIC_DEBUG |
— |
# 임베디드 LCD 최소 설정 예시 (.config 발췌)
CONFIG_FB=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FONT_8x16=y
CONFIG_LOGO=y
# CONFIG_FB_EFI is not set ← 임베디드에서 불필요
# CONFIG_FB_VESA is not set ← BIOS 없음
CONFIG_FB_SIMPLE=y ← 부트로더 콘솔 표시용
# SPI 디스플레이 추가 설정
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_IMAGEBLIT=y
CONFIG_FB_SYS_FOPS=y
CONFIG_FB_DEFERRED_IO=y
참고자료
커널 공식 문서
Documentation/fb/framebuffer.rst— 프레임버퍼 개요Documentation/fb/api.rst— fbdev API 레퍼런스Documentation/fb/fbcon.rst— fbcon 문서Documentation/fb/deferred_io.rst— Deferred I/ODocumentation/fb/vesafb.rst— VESA 프레임버퍼Documentation/fb/efifb.rst— EFI 프레임버퍼Documentation/fb/simplefb.rst— Simple 프레임버퍼
커널 소스 경로
drivers/video/fbdev/core/fbmem.c— fbdev 코어 (등록, ioctl 디스패치)drivers/video/fbdev/core/fbcon.c— fbcon (프레임버퍼 콘솔)drivers/video/fbdev/core/fb_defio.c— Deferred I/O 프레임워크drivers/video/fbdev/vfb.c— Virtual Framebufferdrivers/video/fbdev/efifb.c— EFI 프레임버퍼drivers/video/fbdev/vesafb.c— VESA 프레임버퍼drivers/video/fbdev/simplefb.c— Simple 프레임버퍼include/linux/fb.h— 커널 내부 fbdev 헤더include/uapi/linux/fb.h— 사용자 공간 API 헤더
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.